From ef0ed0281ca127fb91703cd52b4497c0fc797a86 Mon Sep 17 00:00:00 2001 From: pikann Date: Mon, 18 May 2026 16:18:19 +0700 Subject: [PATCH 1/2] ci: implement ci pipeline --- .github/workflows/backend-pr-ci.yml | 101 +++++++++++++++++++++++++++ .github/workflows/frontend-pr-ci.yml | 53 ++++++++++++++ .github/workflows/mcp-pr-ci.yml | 53 ++++++++++++++ 3 files changed, 207 insertions(+) create mode 100644 .github/workflows/backend-pr-ci.yml create mode 100644 .github/workflows/frontend-pr-ci.yml create mode 100644 .github/workflows/mcp-pr-ci.yml diff --git a/.github/workflows/backend-pr-ci.yml b/.github/workflows/backend-pr-ci.yml new file mode 100644 index 0000000..694d4df --- /dev/null +++ b/.github/workflows/backend-pr-ci.yml @@ -0,0 +1,101 @@ +name: backend-pr-ci + +on: + pull_request: + paths: + - "backend/**" + - ".github/workflows/backend-pr-ci.yml" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: backend-pr-ci-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + GO_VERSION: "1.24" + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + timeout-minutes: 10 + + defaults: + run: + working-directory: backend + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + cache-dependency-path: backend/go.sum + + - name: Install golangci-lint + run: go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest + + - name: Run golangci-lint + run: golangci-lint run --timeout=5m + + build: + name: Build + runs-on: ubuntu-latest + timeout-minutes: 10 + + defaults: + run: + working-directory: backend + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + cache-dependency-path: backend/go.sum + + - name: Build WASM + run: GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o github.wasm . + + test: + name: Tests + runs-on: ubuntu-latest + timeout-minutes: 15 + + defaults: + run: + working-directory: backend + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + cache-dependency-path: backend/go.sum + + - name: Run tests (race detector) + run: go test -race -timeout 60s -coverprofile=coverage.out $(go list ./...) + + - name: Upload coverage report + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: backend/coverage.out + retention-days: 7 \ No newline at end of file diff --git a/.github/workflows/frontend-pr-ci.yml b/.github/workflows/frontend-pr-ci.yml new file mode 100644 index 0000000..d378789 --- /dev/null +++ b/.github/workflows/frontend-pr-ci.yml @@ -0,0 +1,53 @@ +name: frontend-pr-ci + +on: + pull_request: + paths: + - "frontend/**" + - ".github/workflows/frontend-pr-ci.yml" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: frontend-pr-ci-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + web-quality: + name: Typecheck and build frontend + runs-on: ubuntu-latest + timeout-minutes: 20 + + defaults: + run: + working-directory: frontend + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: "1.2.23" + + - name: Cache Bun packages + uses: actions/cache@v4 + with: + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('frontend/bun.lock') }} + restore-keys: | + ${{ runner.os }}-bun- + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Typecheck + run: bun run typecheck + + - name: Build production bundle + run: bun run build \ No newline at end of file diff --git a/.github/workflows/mcp-pr-ci.yml b/.github/workflows/mcp-pr-ci.yml new file mode 100644 index 0000000..43c8ad9 --- /dev/null +++ b/.github/workflows/mcp-pr-ci.yml @@ -0,0 +1,53 @@ +name: mcp-pr-ci + +on: + pull_request: + paths: + - "mcp/**" + - ".github/workflows/mcp-pr-ci.yml" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: mcp-pr-ci-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + mcp-quality: + name: Typecheck and build MCP server + runs-on: ubuntu-latest + timeout-minutes: 10 + + defaults: + run: + working-directory: mcp + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: "1.2.23" + + - name: Cache Bun packages + uses: actions/cache@v4 + with: + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-mcp-${{ hashFiles('mcp/bun.lock') }} + restore-keys: | + ${{ runner.os }}-bun-mcp- + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Typecheck + run: bun run typecheck + + - name: Build + run: bun run build \ No newline at end of file From c045b4344a8c99d6f277eda1c56625211cf63052 Mon Sep 17 00:00:00 2001 From: pikann Date: Mon, 18 May 2026 16:41:50 +0700 Subject: [PATCH 2/2] test: add integration tests for GitHub plugin functionality --- backend/plugin_test.go | 89 ++++++++++++++++++++++++++++++++++++++++++ backend/scanner.go | 11 ------ 2 files changed, 89 insertions(+), 11 deletions(-) create mode 100644 backend/plugin_test.go diff --git a/backend/plugin_test.go b/backend/plugin_test.go new file mode 100644 index 0000000..6f725bd --- /dev/null +++ b/backend/plugin_test.go @@ -0,0 +1,89 @@ +package main + +import ( + "encoding/json" + "testing" + + plugin "github.com/Paca-AI/plugin-sdk-go" + "github.com/Paca-AI/plugin-sdk-go/plugintest" +) + +// ── Helpers ─────────────────────────────────────────────────────────────────── + +const ( + testProjectID = "project-1" + testTaskID = "task-1" +) + +func setupPlugin(t *testing.T) *plugintest.Context { + t.Helper() + tc := plugintest.NewContext(t) + + // Seed the shared tables referenced by FK. + tc.DB.SeedRows("tasks", []string{"id", "project_id", "deleted_at"}, [][]any{ + {testTaskID, testProjectID, nil}, + }) + + // Seed empty plugin tables so queries return empty result sets instead of errors. + tc.DB.SeedRows("github_integrations", + []string{"id", "project_id", "access_token_enc", "created_at", "updated_at"}, + nil) + tc.DB.SeedRows("github_repositories", + []string{"id", "project_id", "integration_id", "owner", "repo_name", "full_name", + "webhook_id", "webhook_secret_enc", "default_branch", "created_at", "updated_at"}, + nil) + tc.DB.SeedRows("github_pull_requests", + []string{"id", "project_id", "repo_id", "pr_number", "github_pr_id", "title", + "state", "html_url", "head_branch", "base_branch", "author", "merged_at", "created_at", "updated_at"}, + nil) + tc.DB.SeedRows("github_task_pr_links", + []string{"id", "task_id", "pull_request_id", "created_at"}, + nil) + tc.DB.SeedRows("github_task_branches", + []string{"id", "task_id", "repo_id", "branch_name", "created_at"}, + nil) + + var p githubPlugin + if err := p.Init(tc.PluginContext()); err != nil { + t.Fatal("Init failed:", err) + } + return tc +} + +func callerReq() plugintest.Request { + return plugintest.Request{ + Caller: plugin.CallerIdentity{ + ProjectID: testProjectID, + CallerID: "member-1", + CallerRole: "PROJECT_MEMBER", + }, + PathParams: map[string]string{}, + } +} + +// ── Integration tests ───────────────────────────────────────────────────────── + +func TestGetIntegration_NotConnected(t *testing.T) { + tc := setupPlugin(t) + res := tc.Call("GET", "/github", callerReq()) + + if res.StatusCode != 200 { + t.Fatalf("expected 200, got %d: %s", res.StatusCode, res.BodyString()) + } + var env struct { + Success bool `json:"success"` + Data integrationResponse `json:"data"` + } + if err := json.Unmarshal(res.Body, &env); err != nil { + t.Fatal(err) + } + if !env.Success { + t.Fatal("expected success=true") + } + if env.Data.Connected { + t.Fatal("expected Connected=false when no token is set") + } + if env.Data.ProjectID != testProjectID { + t.Fatalf("expected project_id=%s, got %s", testProjectID, env.Data.ProjectID) + } +} diff --git a/backend/scanner.go b/backend/scanner.go index cb888d6..389565c 100644 --- a/backend/scanner.go +++ b/backend/scanner.go @@ -55,14 +55,3 @@ func (s *scanner) int64Val(col string) int64 { func (s *scanner) intVal(col string) int { return int(s.int64Val(col)) } - -func (s *scanner) boolVal(col string) bool { - i, ok := s.idx[col] - if !ok || i >= len(s.row) || s.row[i] == nil { - return false - } - if v, ok := s.row[i].(bool); ok { - return v - } - return false -}