Phase 1 test coverage for the three 0% packages plus agentci/ improvement: - git/ (0% -> 79.5%): RepoStatus methods, status parsing with real temp repos, multi-repo parallel status, Push/Pull error paths, ahead/behind with bare remote, context cancellation, GitError, IsNonFastForward, service DirtyRepos/AheadRepos filtering - forge/ (0% -> 91.2%): All SDK wrapper functions tested via httptest mock server — client creation, repos, issues, PRs, labels, webhooks, orgs, meta, config resolution, SetPRDraft raw HTTP endpoint - gitea/ (0% -> 89.2%): All SDK wrapper functions tested via httptest mock server — client creation, repos, issues, PRs, meta, config resolution - agentci/ (56% -> 94.5%): Clotho DeterminePlan all code paths, security helpers (SanitizePath, EscapeShellArg, SecureSSHCommand, MaskToken) Key findings documented in FINDINGS.md: - Forgejo SDK validates token via HTTP on NewClient() - SDK route patterns differ from public API docs (/org/ vs /orgs/) - Gitea SDK requires auth token for GitHub mirror creation - Config resolution priority verified: config file < env vars < flags Co-Authored-By: Charon <developers@lethean.io>
5.6 KiB
FINDINGS.md — go-scm Research & Discovery
2026-02-20: Initial Assessment (Virgil)
Origin
Extracted from forge.lthn.ai/core/go on 19 Feb 2026 as part of the go-ai split. Contains all SCM integration, CI dispatch, and data collection code.
Package Inventory
| Package | Files | LOC (approx) | Tests | Coverage |
|---|---|---|---|---|
| forge/ | 9 | ~900 | 0 | 0% |
| gitea/ | 5 | ~500 | 0 | 0% |
| git/ | 2 | ~400 | 0 | 0% |
| agentci/ | 4 | ~300 | 1 | partial |
| jobrunner/ | 4 + handlers/ | ~1,500 | several | partial |
| collect/ | 12 | ~5,000 | 8 | unknown |
Dependencies
codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2— Forgejo API (v2)code.gitea.io/sdk/gitea— Gitea APIforge.lthn.ai/core/go— Framework (log, config, viper)github.com/stretchr/testify— Testing
Key Observations
- forge/ and gitea/ are structurally identical — Same pattern: client.go, config.go, repos.go, issues.go, meta.go. Could share an interface.
- Zero tests in forge/, gitea/, git/ — Most critical gap. These are the foundational packages.
- collect/ has the most tests — 8 test files covering all collectors. Coverage unknown.
- jobrunner/handlers/ has test files — dispatch_test.go, enable_auto_merge_test.go, publish_draft_test.go, resolve_threads_test.go, send_fix_command_test.go, tick_parent_test.go. Quality unknown.
- agentci/ Clotho Protocol — Dual-run verification for critical repos. Currently basic (string match on repo name). Needs more sophisticated policy engine.
Auth Resolution
Both forge/ and gitea/ resolve auth from:
~/.core/config.yaml(forge.token/forge.url or gitea.token/gitea.url)- Environment variables (FORGE_TOKEN/FORGE_URL or GITEA_TOKEN/GITEA_URL)
- CLI flag overrides (highest priority)
This is handled via core/go's viper integration.
Infrastructure Context
- Forge (
forge.lthn.ai) — Production Forgejo instance on de2. Full IP/intel/research. - Gitea (
git.lthn.ai) — Public mirror with reduced data. Breach-safe. - Split policy: Forge = source of truth, Gitea = public-facing mirror with sensitive data stripped.
2026-02-20: Phase 1 Test Coverage (Charon)
Coverage Results After Phase 1
| Package | Before | After | Target | Notes |
|---|---|---|---|---|
| forge/ | 0% | 91.2% | 50% | Exceeded target |
| gitea/ | 0% | 89.2% | 50% | Exceeded target |
| git/ | 0% | 79.5% | 80% | Remaining ~0.5% is framework service integration |
| agentci/ | 56% | 94.5% | 60% | Added clotho.go + security.go tests |
| collect/ | 57.3% | 57.3% | — | Audited, no changes (HTTP-dependent collectors) |
| jobrunner/ | 86.4% | 86.4% | — | Already above 60%, no changes needed |
| jobrunner/forgejo | 73.3% | 73.3% | — | Already above 60% |
| jobrunner/handlers | 61.6% | 61.6% | — | Already above 60% |
SDK Testability Findings
-
Forgejo SDK (
forgejo/v2) validation on client creation:NewClient()makes an HTTP GET to/api/v1/versionduring construction. All tests that create a client need anhttptest.Serverwith at least a/api/v1/versionhandler. This means no zero-cost unit test instantiation — every forge/ test needs a mock server. -
Forgejo SDK route patterns differ from the public API docs: The SDK uses
/api/v1/org/{name}/repos(singularorg) forCreateOrgRepo, but/api/v1/orgs/{name}/repos(pluralorgs) forListOrgRepos. This was discovered during test construction and would bite anyone writing integration tests. -
Gitea SDK mirror validation:
CreateMirrorwithService: GitServiceGithubrequires a non-emptyAuthToken. Without it, the SDK rejects the request locally before sending to the server. Tests must always provide a token. -
forge/ and gitea/ are testable with httptest: Despite being SDK wrappers, coverage above 89% was achievable for both packages using
net/http/httptest. The mock server approach covers: client creation, error handling, state mapping logic (issue states, PR merge styles), pagination termination, config resolution, and raw HTTP endpoints (SetPRDraft). -
git/ requires real git repos for integration testing:
t.TempDir()+git init+git commitprovides clean, isolated test environments. ThegetAheadBehindfunction requires a bare remote + clone setup to test properly. -
git/service.go framework dependency:
NewService,OnStartup,handleQuery, andhandleTaskdepend onframework.Corefromcore/go. These are better tested in integration tests (Phase 3). TheDirtyRepos(),AheadRepos(), andStatus()helper methods are tested by directly settinglastStatus. -
Thin SDK wrappers: Most forge/ and gitea/ functions are 3-5 line SDK pass-throughs (call SDK, check error, return). Despite being thin, they were all testable via mock server because the SDK sends real HTTP requests. No function was skipped as "untestable".
-
agentci/security.go
SanitizePath:filepath.Base("../secret")returns"secret", which passes validation. This meansSanitizePathprotects against path traversal by stripping the directory component, not by rejecting the input. This is correct behaviour — documented in test.
Config Resolution Verified
Both forge/ and gitea/ follow the same priority order:
- Config file (
~/.core/config.yaml) — lowest priority - Environment variables (
FORGE_URL/FORGE_TOKENorGITEA_URL/GITEA_TOKEN) - Flag overrides — highest priority
- Default URL when nothing configured (
http://localhost:4000for forge,https://gitea.snider.devfor gitea)
Tests must use t.Setenv("HOME", t.TempDir()) to isolate from the real config file on the development machine.