package main import ( "encoding/json" "net/http/httptest" "os" "path/filepath" "strings" "testing" ) func TestDiagramRevisionHistoryAndRestore(t *testing.T) { c, _ := loadUseCases("..") ds := &diagramStore{dir: t.TempDir()} first := draftDiagram(c.UC["W-49"]) first.Nodes[0].Title = "version one" first, err := ds.save("W-49", first) if err != nil { t.Fatal(err) } second := first second.Nodes = append([]DiagramNode{}, first.Nodes...) second.Nodes[0].Title = "version two" second, err = ds.save("W-49", second) if err != nil { t.Fatal(err) } list, err := ds.revisions("W-49") if err != nil || len(list) != 2 { t.Fatal("missing history", err) } restarted := &diagramStore{dir: ds.dir} restored, err := restarted.restore("W-49", second.Revision, first.Revision) if err != nil || restored.Revision != 3 || restored.Nodes[0].Title != "version one" { t.Fatal("restore must create new revision", err) } list, err = restarted.revisions("W-49") if err != nil || len(list) != 3 || list[2].RestoredFrom != 1 { t.Fatal("restore lost history", err) } if _, err = restarted.restore("W-49", 2, 1); err != errDiagramConflict { t.Fatal("stale restore accepted") } if _, err = restarted.restore("W-49", 3, 999); err != errDiagramRevisionNotFound { t.Fatal("unknown revision accepted") } snapshot, err := restarted.revision("W-49", 2) if err != nil || snapshot.Nodes[0].Title != "version two" { t.Fatal("old snapshot changed", err) } } func TestDiagramHistoryPreservesExistingFile(t *testing.T) { c, _ := loadUseCases("..") d := draftDiagram(c.UC["W-49"]) d.Revision = 7 dir := t.TempDir() os.MkdirAll(filepath.Join(dir, "diagrams"), 0755) b, _ := json.Marshal(d) os.WriteFile(filepath.Join(dir, "diagrams", "W-49.json"), b, 0600) ds := &diagramStore{dir: dir} list, err := ds.revisions("W-49") if err != nil || len(list) != 1 || list[0].Revision != 7 { t.Fatal("existing revision missing", err) } d.Nodes[0].Title = "next" if _, err = ds.save("W-49", d); err != nil { t.Fatal(err) } list, err = ds.revisions("W-49") if err != nil || len(list) != 2 || list[0].Revision != 7 { t.Fatal("existing revision was overwritten", err) } } func TestDiagramHistoryHTTP(t *testing.T) { old := repoRoot repoRoot = ".." defer func() { repoRoot = old }() mux := newMux(NewStore(t.TempDir()), NewHub()) c, _ := loadUseCases("..") b, _ := json.Marshal(draftDiagram(c.UC["W-49"])) req := httptest.NewRequest("PUT", "/api/use-cases/W-49/diagram", strings.NewReader(string(b))) req.Header.Set("Origin", "http://example.com") w := httptest.NewRecorder() mux.ServeHTTP(w, req) if w.Code != 200 { t.Fatal(w.Code) } for _, path := range []string{"/api/use-cases/W-49/revisions", "/api/use-cases/W-49/revisions/1"} { w = httptest.NewRecorder() mux.ServeHTTP(w, httptest.NewRequest("GET", path, nil)) if w.Code != 200 { t.Fatal(path, w.Code) } } for _, tc := range []struct { origin, body string code int }{{"http://evil.test", `{"revision":1,"target_revision":1}`, 403}, {"http://example.com", `{"revision":1,"target_revision":1}`, 200}, {"http://example.com", `{"revision":1,"target_revision":1}`, 409}, {"http://example.com", `{"revision":2,"target_revision":999}`, 404}, {"http://example.com", `{} {}`, 400}} { req = httptest.NewRequest("POST", "/api/use-cases/W-49/restore", strings.NewReader(tc.body)) req.Header.Set("Origin", tc.origin) w = httptest.NewRecorder() mux.ServeHTTP(w, req) if w.Code != tc.code { t.Fatalf("restore %d expected %d: %s", w.Code, tc.code, w.Body.String()) } } }