Files
XingfenD 4e467e1136 feat: implement proto-gen platform
- Config loader with env var expansion and validation
- SQLite database with tasks and toolchain_versions tables
- Gitea REST API client for repository and branch operations
- K8s client for Job lifecycle management (with noop fallback for local dev)
- REST API: tasks CRUD, retry, plugins, version management
- Worker/scheduler with polling, concurrency control, and job tracking
- Static frontend: generate, tasks, settings pages
- Toolchain images: Go and Node.js Dockerfiles with entrypoint scripts
- Helm chart with deployment, service, configmap, ingress, PVC
- Multi-stage Dockerfile for service
- Unit tests for config, database, gitclient, k8s, api, worker
- E2E tests for idempotency, version switch, failure recovery
2026-08-10 16:54:30 +08:00

128 lines
3.3 KiB
Go

package api
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"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) *Server {
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 NewServer(db, cfg, &mockGitClient{})
}
func TestHandleCreateTask(t *testing.T) {
srv := newTestServer(t)
body, _ := json.Marshal(CreateTaskRequest{
Language: "go",
ProtoRepo: "test-owner/yoresee_doc",
ProtoBranch: "main",
})
req := httptest.NewRequest("POST", "/api/v1/tasks", bytes.NewReader(body))
w := httptest.NewRecorder()
srv.ServeHTTP(w, req)
if w.Code != http.StatusCreated {
t.Errorf("status = %d, want %d", w.Code, http.StatusCreated)
}
var task models.Task
json.NewDecoder(w.Body).Decode(&task)
if task.Language != "go" {
t.Errorf("Language = %q, want %q", task.Language, "go")
}
if task.Status != models.StatusPending {
t.Errorf("Status = %q, want %q", task.Status, models.StatusPending)
}
}
func TestHandleCreateTask_UnsupportedLanguage(t *testing.T) {
srv := newTestServer(t)
body, _ := json.Marshal(CreateTaskRequest{
Language: "python",
ProtoRepo: "test-owner/yoresee_doc",
ProtoBranch: "main",
})
req := httptest.NewRequest("POST", "/api/v1/tasks", bytes.NewReader(body))
w := httptest.NewRecorder()
srv.ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
t.Errorf("status = %d, want %d", w.Code, http.StatusBadRequest)
}
}
func TestHandleListPlugins(t *testing.T) {
srv := newTestServer(t)
req := httptest.NewRequest("GET", "/api/v1/plugins", nil)
w := httptest.NewRecorder()
srv.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("status = %d, want %d", w.Code, http.StatusOK)
}
var plugins []config.PluginConfig
json.NewDecoder(w.Body).Decode(&plugins)
if len(plugins) != 2 {
t.Errorf("len(plugins) = %d, want 2", len(plugins))
}
}
func TestHandleGetTask_NotFound(t *testing.T) {
srv := newTestServer(t)
req := httptest.NewRequest("GET", "/api/v1/tasks/nonexistent", nil)
w := httptest.NewRecorder()
srv.ServeHTTP(w, req)
if w.Code != http.StatusNotFound {
t.Errorf("status = %d, want %d", w.Code, http.StatusNotFound)
}
}