package main import ( "bytes" "os" "path/filepath" "strings" "testing" "sourcecraft.dev/bigbes/sr-ht-spec/core" "sourcecraft.dev/bigbes/sr-ht-spec/service" ) // TestParseDocProposeTakesFlagsAfterThePositionals is the reason // parseFlagsAnywhere exists: the natural way to type this command puts the // space and the file first, and Go's flag package would stop there and drop // every flag that follows. func TestParseDocProposeTakesFlagsAfterThePositionals(t *testing.T) { o, err := parseDocPropose([]string{ "~bigbes/rfcs", "rfc-0001.md", "--title", "RFC-0001", "--rationale", "because", "--as", "rfcs/rfc-0001.md", }) if err != nil { t.Fatalf("parseDocPropose: %v", err) } if o.space.Owner != "bigbes" || o.space.Name != "rfcs" { t.Errorf("space = %+v want ~bigbes/rfcs", o.space) } if len(o.files) != 1 || o.files[0] != "rfc-0001.md" { t.Errorf("files = %v want [rfc-0001.md]", o.files) } if o.title != "RFC-0001" || o.rationale != "because" || o.as != "rfcs/rfc-0001.md" { t.Errorf("flags after the positionals were dropped: %+v", o) } } func TestParseDocProposeTakesFlagsBetweenPositionals(t *testing.T) { o, err := parseDocPropose([]string{ "--proposal", "7", "~bigbes/rfcs", "--message", "add two", "a.md", "b.md", }) if err != nil { t.Fatalf("parseDocPropose: %v", err) } if o.proposalID != 7 || o.message != "add two" { t.Errorf("flags lost: %+v", o) } if len(o.files) != 2 || o.files[0] != "a.md" || o.files[1] != "b.md" { t.Errorf("files = %v want [a.md b.md] in order", o.files) } } func TestParseDocProposeMintsASessionWhenNoneIsGiven(t *testing.T) { first, err := parseDocPropose([]string{"~bigbes/rfcs", "a.md"}) if err != nil { t.Fatalf("parseDocPropose: %v", err) } second, err := parseDocPropose([]string{"~bigbes/rfcs", "a.md"}) if err != nil { t.Fatalf("parseDocPropose: %v", err) } if first.session == "" || second.session == "" { t.Fatal("no session id was minted; the write would be refused for missing provenance") } if first.session == second.session { t.Errorf("two invocations reported the same session %q", first.session) } if !strings.HasPrefix(first.session, "cli-") { t.Errorf("session %q is not marked as coming from this command", first.session) } if first.agent != "specsrht-cli" { t.Errorf("agent = %q want the default specsrht-cli", first.agent) } } func TestParseDocProposeRejectsBadInvocations(t *testing.T) { tests := map[string][]string{ "no arguments at all": {}, "a space but no file": {"~bigbes/rfcs"}, "a space with no name": {"~bigbes", "a.md"}, "--as with two files": {"~bigbes/rfcs", "a.md", "b.md", "--as", "one.md"}, "a negative proposal id": {"~bigbes/rfcs", "a.md", "--proposal", "-1"}, "a flag that is not ours": {"~bigbes/rfcs", "a.md", "--titel", "typo"}, } for name, args := range tests { if _, err := parseDocPropose(args); err == nil { t.Errorf("parseDocPropose accepted %s: %v", name, args) } } } func TestLoadWritesDefaultsThePathToTheBaseName(t *testing.T) { dir := t.TempDir() local := filepath.Join(dir, "rfc-0001-spec-sr-ht.md") if err := os.WriteFile(local, []byte("# hello\n"), 0o644); err != nil { t.Fatal(err) } writes, err := loadWrites(docProposeOpts{files: []string{local}}) if err != nil { t.Fatalf("loadWrites: %v", err) } if len(writes) != 1 { t.Fatalf("got %d writes want 1", len(writes)) } if writes[0].Path != "rfc-0001-spec-sr-ht.md" { t.Errorf("path = %q want the base name, not the local directory", writes[0].Path) } if string(writes[0].Content) != "# hello\n" { t.Errorf("content = %q", writes[0].Content) } } // TestLoadWritesRefusesAPathThisCommandInvented guards the one path this layer // makes up rather than receives: a base name that is not a document path must // be refused by name, before a proposal row exists to leave behind. func TestLoadWritesRefusesAPathThisCommandInvented(t *testing.T) { dir := t.TempDir() local := filepath.Join(dir, "notes.txt") if err := os.WriteFile(local, []byte("x"), 0o644); err != nil { t.Fatal(err) } if _, err := loadWrites(docProposeOpts{files: []string{local}}); err == nil { t.Error("loadWrites accepted a non-.md base name") } if _, err := loadWrites(docProposeOpts{files: []string{local}, as: "../escape.md"}); err == nil { t.Error("loadWrites accepted a path that escapes the space") } } func TestLoadWritesNamesTheFileItCouldNotRead(t *testing.T) { missing := filepath.Join(t.TempDir(), "gone.md") _, err := loadWrites(docProposeOpts{files: []string{missing}}) if err == nil { t.Fatal("loadWrites accepted a missing file") } if !strings.Contains(err.Error(), "gone.md") { t.Errorf("the error does not name the file:\n%v", err) } } func TestPrintProposeResultLeadsToTheURL(t *testing.T) { var buf bytes.Buffer res := service.ProposeResult{ Proposal: service.Proposal{ ID: 7, State: core.StateOpen, Branch: "proposals/7", BaseRev: "0123456789abcdef0123456789abcdef01234567", }, URL: "https://spec.srht.bigb.es/~bigbes/rfcs/p/7", } writes := []service.DocumentWrite{{Path: "rfc-0001.md", Content: []byte("abc")}} if err := printProposeResult(&buf, res, writes); err != nil { t.Fatalf("printProposeResult: %v", err) } out := buf.String() for _, want := range []string{"proposal 7", "rfc-0001.md (3 bytes)", "proposals/7", res.URL} { if !strings.Contains(out, want) { t.Errorf("output does not mention %q:\n%s", want, out) } } if !strings.HasSuffix(strings.TrimRight(out, "\n"), res.URL) { t.Errorf("the URL is not the last line an operator sees:\n%s", out) } } func TestPrintProposeResultSaysWhenPolicyLandedIt(t *testing.T) { var buf bytes.Buffer res := service.ProposeResult{ Proposal: service.Proposal{ID: 8, State: core.StateMerged, Branch: "proposals/8"}, URL: "https://spec.srht.bigb.es/~bigbes/rfcs/p/8", Merged: true, } if err := printProposeResult(&buf, res, nil); err != nil { t.Fatalf("printProposeResult: %v", err) } if !strings.Contains(buf.String(), "auto-merged") { t.Errorf("a policy-merged write reads as if it is waiting for review:\n%s", buf.String()) } } func TestRunDocRejectsAnUnknownSubcommandBeforeTheDatabase(t *testing.T) { if err := runDoc(nil); err == nil || err.Error() != docUsage { t.Errorf("empty invocation did not print the usage line: %v", err) } err := runDoc([]string{"upload", "~bigbes/rfcs", "a.md"}) if err == nil || !strings.Contains(err.Error(), "propose") { t.Errorf("unknown subcommand error does not name the real one: %v", err) } }