mirror of
https://github.com/alibaba/open-code-review.git
synced 2026-10-22 06:40:12 +00:00
Some checks failed
CI / test (push) Waiting to run
CI / windows (push) Waiting to run
CI / cross-compile (amd64, darwin) (push) Waiting to run
CI / cross-compile (amd64, windows) (push) Waiting to run
CI / cross-compile (arm64, darwin) (push) Waiting to run
CI / cross-compile (arm64, linux) (push) Waiting to run
CI / cross-compile (arm64, windows) (push) Waiting to run
CodeQL Advanced / Analyze (go) (push) Waiting to run
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Deploy Pages / build (push) Waiting to run
Deploy Pages / deploy (push) Blocked by required conditions
plugin-contract / plugin-contract (push) Waiting to run
Action Contract / action-contract (push) Has been cancelled
Sessions accumulate in ~/.opencodereview/sessions/ with no way to remove one short of deleting files by hand. The Viewer stays read-only by design, so deletion belongs in the CLI, where the caller already owns the files. The session id is enough on its own: the command looks for it under every repository, so it works from anywhere. That a session is stored under a directory derived from its repository is an implementation detail, and making the caller stand in the right repository to delete by id pushes that detail onto them. --repo narrows the search rather than enabling it. An id saved for more than one repository lists the candidates and deletes nothing. encodeRepoPath replaces separators with "-", so two repository paths can share one sessions directory and one id can name two different files; choosing between them would be a coin flip on a file that cannot be recovered. The command prints the session's repository, branch, start time, file count and comment count in the same format `ocr session list` uses, then asks to confirm. A non-interactive stdin answers no, so `--yes` has to be passed deliberately from a pipeline or CI. Reports an unreadable session rather than folding it into "corrupt" -- a session whose metadata cannot be parsed is still deletable, since that is the record most likely to need removing. Documented in all five locales of the CLI reference. Co-authored-by: Qiyuanqiii <267806965+Qiyuanqiii@users.noreply.github.com> Co-authored-by: wu21-web <wu2196674@icloud.com>
145 lines
5 KiB
Go
145 lines
5 KiB
Go
// SPDX-License-Identifier: Apache-2.0
|
|
// Copyright 2026 alibaba/open-code-review Contributors
|
|
|
|
package main
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestCLIReferenceDocumentsSessionCompare(t *testing.T) {
|
|
for _, locale := range []string{"en", "zh", "ja", "ru"} {
|
|
t.Run(locale, func(t *testing.T) {
|
|
path := filepath.Join("..", "..", "pages", "src", "content", "docs", locale, "cli-reference.md")
|
|
body, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatalf("read %s: %v", path, err)
|
|
}
|
|
for _, want := range []string{
|
|
"`ocr session compare <before> <after>`", // command-summary table row
|
|
"### `ocr session compare`", // reference section
|
|
"`ocr session diff <before> <after>`", // alias
|
|
"not_reviewed", // the JSON bucket that is easy to forget
|
|
} {
|
|
if !strings.Contains(string(body), want) {
|
|
t.Errorf("%s: missing %q", path, want)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCLIReferenceDocumentsSessionExport(t *testing.T) {
|
|
for _, locale := range []string{"en", "zh", "ja", "ru", "ko"} {
|
|
t.Run(locale, func(t *testing.T) {
|
|
path := filepath.Join("..", "..", "pages", "src", "content", "docs", locale, "cli-reference.md")
|
|
body, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatalf("read %s: %v", path, err)
|
|
}
|
|
for _, want := range []string{
|
|
"`ocr session export [id]`", // command-summary table row
|
|
"### `ocr session export`", // reference section
|
|
"ocr session export -o review.html", // the no-id form, which is the default
|
|
"`--output <path>`", // the flag that makes it archivable
|
|
} {
|
|
if !strings.Contains(string(body), want) {
|
|
t.Errorf("%s: missing %q", path, want)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCLIReferenceUsesSubtaskUnit(t *testing.T) {
|
|
type localePin struct {
|
|
locale string
|
|
term string
|
|
}
|
|
pins := []localePin{
|
|
{locale: "en", term: "subtask"},
|
|
{locale: "zh", term: "子任务"}, // allow-non-english: locale subtask term from issue 1273
|
|
{locale: "ja", term: "サブタスク"}, // allow-non-english: locale subtask term from issue 1273
|
|
{locale: "ko", term: "서브태스크"}, // allow-non-english: locale subtask term from issue 1273
|
|
{locale: "ru", term: "подзадач"}, // allow-non-english: locale subtask stem from issue 1273
|
|
}
|
|
flagMarkers := []string{
|
|
"| `--no-filter` |",
|
|
"| `--concurrency <n>` |",
|
|
"| `--timeout <minutes>` |",
|
|
"| `--max-tools <n>` |",
|
|
"| `--max-tokens <n>` |",
|
|
}
|
|
leakedConcurrencyUnits := []string{
|
|
"file group", "file groups", "files reviewed", "file scans",
|
|
"文件组", "文件数", // allow-non-english: old zh file/file-group unit
|
|
"ファイルグループ", "ファイルの最大", // allow-non-english: old ja file/file-group unit
|
|
"파일 그룹", // allow-non-english: old ko file-group unit
|
|
"число файлов", "группу файлов", "группы файлов", // allow-non-english: old ru file/file-group unit
|
|
}
|
|
|
|
for _, pin := range pins {
|
|
t.Run(pin.locale, func(t *testing.T) {
|
|
path := filepath.Join("..", "..", "pages", "src", "content", "docs", pin.locale, "cli-reference.md")
|
|
raw, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatalf("read %s: %v", path, err)
|
|
}
|
|
body := string(raw)
|
|
for _, marker := range flagMarkers {
|
|
row, ok := firstLineContaining(body, marker)
|
|
if !ok {
|
|
t.Errorf("%s: missing flag row %q", path, marker)
|
|
continue
|
|
}
|
|
if !strings.Contains(row, pin.term) {
|
|
t.Errorf("%s: row %q does not use %q", path, row, pin.term)
|
|
}
|
|
if marker == "| `--concurrency <n>` |" {
|
|
for _, leaked := range leakedConcurrencyUnits {
|
|
if strings.Contains(row, leaked) {
|
|
t.Errorf("%s: --concurrency still describes a file/file-group count (%q): %s", path, leaked, row)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func firstLineContaining(body, marker string) (string, bool) {
|
|
for _, line := range strings.Split(body, "\n") {
|
|
if strings.Contains(line, marker) {
|
|
return line, true
|
|
}
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
func TestCLIReferenceDocumentsSessionRm(t *testing.T) {
|
|
for _, locale := range []string{"en", "zh", "ja", "ru", "ko"} {
|
|
t.Run(locale, func(t *testing.T) {
|
|
path := filepath.Join("..", "..", "pages", "src", "content", "docs", locale, "cli-reference.md")
|
|
body, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatalf("read %s: %v", path, err)
|
|
}
|
|
for _, want := range []string{
|
|
"`ocr session rm <id>`", // command-summary table row
|
|
"### `ocr session rm`", // reference section
|
|
"`ocr session delete <id>`", // alias
|
|
"ocr session rm 9f2c1b4a-7e35-4d61-b2f0-6c8a41d9e72b --yes", // the flag in use, with an id shaped like a real one
|
|
"`--yes`", // the flag itself, however the locale punctuates the row
|
|
"`-y`", // and its shorthand
|
|
"stdin", // the non-interactive rule
|
|
} {
|
|
if !strings.Contains(string(body), want) {
|
|
t.Errorf("%s: missing %q", path, want)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|