package e2e import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "testing" "proto-gen/internal/api" "proto-gen/internal/config" "proto-gen/internal/database" "proto-gen/internal/gitclient" "proto-gen/internal/models" ) type mockGitClient struct{} func (m *mockGitClient) ListRepositories(owner string) ([]gitclient.Repository, error) { return nil, nil } func (m *mockGitClient) CreateRepository(owner, name, desc string, private bool) (*gitclient.Repository, error) { return &gitclient.Repository{Name: name}, nil } func (m *mockGitClient) GetRepository(owner, name string) (*gitclient.Repository, error) { return &gitclient.Repository{Name: name}, nil } func (m *mockGitClient) ListBranches(owner, repo string) ([]gitclient.Branch, error) { return []gitclient.Branch{{Name: "main", Commit: "abc123"}}, nil } func (m *mockGitClient) GetBranch(owner, repo, branch string) (*gitclient.Branch, error) { return &gitclient.Branch{Name: branch, Commit: "abc123"}, nil } func newTestServer(t *testing.T) (*api.Server, *database.DB) { t.Helper() db, err := database.New(t.TempDir() + "/test.db") if err != nil { t.Fatalf("creating db: %v", err) } t.Cleanup(func() { db.Close() }) cfg := &config.Config{ ToolchainRegistry: "registry.example.com/proto-gen", TargetRepoTemplate: "{proto_repo}-gen-{lang}", Plugins: []config.PluginConfig{ {Language: "go", Image: "proto-gen-go:v1.0"}, {Language: "nodejs", Image: "proto-gen-ts:v1.0"}, }, } return api.NewServer(db, cfg, &mockGitClient{}), db } func TestIdempotency(t *testing.T) { srv, db := newTestServer(t) body, _ := json.Marshal(map[string]string{ "language": "go", "proto_repo": "test-owner/yoresee_doc", "proto_branch": "main", }) req := httptest.NewRequest("POST", "/api/v1/tasks", bytes.NewReader(body)) w := httptest.NewRecorder() srv.ServeHTTP(w, req) if w.Code != http.StatusCreated { t.Fatalf("first create: status = %d, want %d", w.Code, http.StatusCreated) } req = httptest.NewRequest("POST", "/api/v1/tasks", bytes.NewReader(body)) w = httptest.NewRecorder() srv.ServeHTTP(w, req) if w.Code != http.StatusCreated { t.Fatalf("second create: status = %d, want %d", w.Code, http.StatusCreated) } tasks, _ := db.ListTasks("", 100, 0) goTasks := 0 for _, task := range tasks { if task.Language == "go" { goTasks++ } } if goTasks != 2 { t.Errorf("go tasks = %d, want 2", goTasks) } } func TestVersionSwitch(t *testing.T) { srv, _ := newTestServer(t) req := httptest.NewRequest("GET", "/api/v1/plugins/go/versions", nil) w := httptest.NewRecorder() srv.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("get version: status = %d, want %d", w.Code, http.StatusOK) } body, _ := json.Marshal(map[string]string{"image": "proto-gen-go:v2.0"}) req = httptest.NewRequest("PUT", "/api/v1/plugins/go/versions", bytes.NewReader(body)) w = httptest.NewRecorder() srv.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("update version: status = %d, want %d", w.Code, http.StatusOK) } var plugin config.PluginConfig json.NewDecoder(w.Body).Decode(&plugin) if plugin.Image != "proto-gen-go:v2.0" { t.Errorf("Image = %q, want %q", plugin.Image, "proto-gen-go:v2.0") } } func TestFailureRecovery(t *testing.T) { srv, db := newTestServer(t) task := &models.Task{ ID: "fail-task-1", Status: models.StatusFailed, Language: "go", ProtoRepo: "test/yoresee_doc", ProtoBranch: "main", TargetRepo: "yoresee_doc-gen-go", ToolchainImage: "proto-gen-go:v1.0", Error: "some error", } db.CreateTask(task) body, _ := json.Marshal(map[string]string{}) req := httptest.NewRequest("POST", "/api/v1/tasks/fail-task-1/retry", bytes.NewReader(body)) w := httptest.NewRecorder() srv.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("retry: status = %d, want %d", w.Code, http.StatusOK) } var retried models.Task json.NewDecoder(w.Body).Decode(&retried) if retried.Status != models.StatusPending { t.Errorf("Status = %q, want %q", retried.Status, models.StatusPending) } if retried.Retries != 1 { t.Errorf("Retries = %d, want 1", retried.Retries) } }