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
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user