package main import ( "context" "strings" "testing" ) // novox/hq issue 1604: the forge's diffpatch answers 500 when an unchanged context line holds a non-ASCII // character; the tool then applies the patch itself. func sectionFake(t *testing.T) *fakeForge { f := newFake(t) f.trees["c0"]["doc.md"] = "# Title\n\nSee § 4 for the rule.\nold line\nend § here\n" f.trees["c0"]["run.sh"] = "#!/bin/sh\necho a\n" f.patchStatus = 500 return f } const sectionPatch = "diff --git a/doc.md b/doc.md\n--- a/doc.md\n+++ b/doc.md\n@@ -2,4 +2,4 @@\n \n See § 4 for the rule.\n-old line\n+new line\n end § here\n" func TestAPatchWhoseContextHoldsANonASCIICharacterIsAppliedByTheToolWhenTheForgeAnswers500(t *testing.T) { f := sectionFake(t) got, err := commit(context.Background(), f.serve(), ask(t, map[string]any{"branch": "fix/1604", "patch": sectionPatch})) if err != nil { t.Fatal(err) } want := "# Title\n\nSee § 4 for the rule.\nnew line\nend § here\n" if tree := f.trees[got.Commit]; tree["doc.md"] != want || tree["README.md"] != "hello\n" { t.Fatalf("tree %v", tree) } if len(f.patches) != 1 || len(f.writes) != 1 || f.writes[0]["new_branch"] != "fix/1604" || f.writes[0]["branch"] != "main" { t.Fatalf("patches %v writes %v", f.patches, f.writes) } if len(got.Files) != 1 || got.Files[0].Operation != "update" || got.Parent != "c0" { t.Fatalf("%+v", got) } if !strings.Contains(strings.Join(got.Said, " "), "applied it") { t.Errorf("not said: %v", got.Said) } } func TestAPatchTheForgeTakesIsStillTheForgesToApply(t *testing.T) { f := sectionFake(t) f.patchStatus = 0 if _, err := commit(context.Background(), f.serve(), ask(t, map[string]any{"branch": "fix/1604", "patch": sectionPatch})); err != nil { t.Fatal(err) } if len(f.writes) != 0 { t.Fatal("the tool applied a patch the forge took") } } func TestAPatchThatCreatesDeletesAndRenamesIsApplied(t *testing.T) { f := sectionFake(t) patch := "diff --git a/new.txt b/new.txt\nnew file mode 100644\n--- /dev/null\n+++ b/new.txt\n@@ -0,0 +1,2 @@\n+one §\n+two\n" + "diff --git a/docs/a.md b/docs/a.md\ndeleted file mode 100644\n--- a/docs/a.md\n+++ /dev/null\n@@ -1 +0,0 @@\n-a\n" + "diff --git a/README.md b/README.txt\nsimilarity index 80%\nrename from README.md\nrename to README.txt\n--- a/README.md\n+++ b/README.txt\n@@ -1 +1 @@\n-hello\n+hello §\n" got, err := commit(context.Background(), f.serve(), ask(t, map[string]any{"branch": "fix/r", "patch": patch})) if err != nil { t.Fatal(err) } tree := f.trees[got.Commit] if tree["new.txt"] != "one §\ntwo\n" || tree["README.txt"] != "hello §\n" { t.Fatalf("tree %v", tree) } if _, has := tree["README.md"]; has { t.Error("the renamed file is still there") } if _, has := tree["docs/a.md"]; has { t.Error("the deleted file is still there") } } func TestAHunkMovedByEarlierLinesIsFoundAndNoNewlineIsKept(t *testing.T) { f := sectionFake(t) f.trees["c0"]["x.txt"] = "pre\npre\nabc §\nd\ne" patch := "--- a/x.txt\n+++ b/x.txt\n@@ -1,3 +1,3 @@\n abc §\n d\n-e\n\\ No newline at end of file\n+f\n\\ No newline at end of file\n" got, err := commit(context.Background(), f.serve(), ask(t, map[string]any{"branch": "fix/n", "patch": patch})) if err != nil { t.Fatal(err) } if g := f.trees[got.Commit]["x.txt"]; g != "pre\npre\nabc §\nd\nf" { t.Fatalf("%q", g) } } func TestAPatchThatDoesNotApplyIsRefusedNamingTheFileAndLine(t *testing.T) { f := sectionFake(t) patch := strings.Replace(sectionPatch, "-old line", "-other line", 1) _, err := commit(context.Background(), f.serve(), ask(t, map[string]any{"branch": "fix/1604", "patch": patch})) if err == nil || !strings.Contains(err.Error(), "doc.md") || !strings.Contains(err.Error(), "at line 2 does not apply") { t.Fatalf("%v", err) } if len(f.writes) != 0 { t.Fatal("something was written") } } func TestAPatchWithAnExecutableFileIsNotAppliedByTheToolAndSaysWhy(t *testing.T) { f := sectionFake(t) for _, patch := range []string{ "diff --git a/s.sh b/s.sh\nnew file mode 100755\n--- /dev/null\n+++ b/s.sh\n@@ -0,0 +1 @@\n+echo §\n", "diff --git a/run.sh b/run.sh\nold mode 100644\nnew mode 100755\n", "diff --git a/run.sh b/run.sh\nindex 1111111..2222222 100755\n--- a/run.sh\n+++ b/run.sh\n@@ -1,2 +1,2 @@\n #!/bin/sh\n-echo a\n+echo b\n", } { _, err := commit(context.Background(), f.serve(), ask(t, map[string]any{"branch": "fix/x", "patch": patch})) if err == nil || !strings.Contains(err.Error(), "the forge did not take the patch") || !strings.Contains(err.Error(), "carries no file mode") { t.Errorf("%v", err) } } if len(f.writes) != 0 { t.Fatal("something was written") } } func TestAClientErrorOfTheForgeIsNotRetriedByTheTool(t *testing.T) { f := sectionFake(t) f.patchStatus = 422 _, err := commit(context.Background(), f.serve(), ask(t, map[string]any{"branch": "fix/1604", "patch": sectionPatch})) if err == nil || len(f.writes) != 0 { t.Fatalf("%v", err) } } func TestADefaultBranchIsStillRefusedForAPatch(t *testing.T) { f := sectionFake(t) _, err := commit(context.Background(), f.serve(), ask(t, map[string]any{"branch": "main", "patch": sectionPatch})) if err == nil || !strings.Contains(err.Error(), "ADR 0266") || len(f.patches)+len(f.writes) != 0 { t.Fatalf("%v", err) } }