2026-03-24 23:31:07 +00:00
|
|
|
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
|
|
|
|
|
|
package agentic
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"net/http"
|
|
|
|
|
"net/http/httptest"
|
2026-03-25 09:19:05 +00:00
|
|
|
"strings"
|
2026-03-24 23:31:07 +00:00
|
|
|
"testing"
|
|
|
|
|
"time"
|
|
|
|
|
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
core "dappco.re/go/core"
|
2026-03-24 23:31:07 +00:00
|
|
|
"dappco.re/go/core/forge"
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// mockScanServer creates a server that handles repo listing and issue listing.
|
|
|
|
|
func mockScanServer(t *testing.T) *httptest.Server {
|
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
|
|
|
|
|
|
// List org repos
|
|
|
|
|
mux.HandleFunc("/api/v1/orgs/core/repos", func(w http.ResponseWriter, r *http.Request) {
|
2026-03-26 06:38:02 +00:00
|
|
|
w.Write([]byte(core.JSONMarshalString([]map[string]any{
|
2026-03-24 23:31:07 +00:00
|
|
|
{"name": "go-io", "full_name": "core/go-io"},
|
|
|
|
|
{"name": "go-log", "full_name": "core/go-log"},
|
|
|
|
|
{"name": "agent", "full_name": "core/agent"},
|
2026-03-26 06:38:02 +00:00
|
|
|
})))
|
2026-03-24 23:31:07 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// List issues for repos
|
|
|
|
|
mux.HandleFunc("/api/v1/repos/core/go-io/issues", func(w http.ResponseWriter, r *http.Request) {
|
2026-03-26 06:38:02 +00:00
|
|
|
w.Write([]byte(core.JSONMarshalString([]map[string]any{
|
2026-03-24 23:31:07 +00:00
|
|
|
{
|
|
|
|
|
"number": 10,
|
|
|
|
|
"title": "Replace fmt.Errorf with E()",
|
|
|
|
|
"labels": []map[string]any{{"name": "agentic"}},
|
|
|
|
|
"assignee": nil,
|
|
|
|
|
"html_url": "https://forge.lthn.ai/core/go-io/issues/10",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"number": 11,
|
|
|
|
|
"title": "Add missing tests",
|
|
|
|
|
"labels": []map[string]any{{"name": "agentic"}, {"name": "help-wanted"}},
|
|
|
|
|
"assignee": map[string]any{"login": "virgil"},
|
|
|
|
|
"html_url": "https://forge.lthn.ai/core/go-io/issues/11",
|
|
|
|
|
},
|
2026-03-26 06:38:02 +00:00
|
|
|
})))
|
2026-03-24 23:31:07 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
mux.HandleFunc("/api/v1/repos/core/go-log/issues", func(w http.ResponseWriter, r *http.Request) {
|
2026-03-26 06:38:02 +00:00
|
|
|
w.Write([]byte(core.JSONMarshalString([]map[string]any{
|
2026-03-24 23:31:07 +00:00
|
|
|
{
|
|
|
|
|
"number": 5,
|
|
|
|
|
"title": "Fix log rotation",
|
|
|
|
|
"labels": []map[string]any{{"name": "bug"}},
|
|
|
|
|
"assignee": nil,
|
|
|
|
|
"html_url": "https://forge.lthn.ai/core/go-log/issues/5",
|
|
|
|
|
},
|
2026-03-26 06:38:02 +00:00
|
|
|
})))
|
2026-03-24 23:31:07 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
mux.HandleFunc("/api/v1/repos/core/agent/issues", func(w http.ResponseWriter, r *http.Request) {
|
2026-03-26 06:38:02 +00:00
|
|
|
w.Write([]byte(core.JSONMarshalString([]map[string]any{})))
|
2026-03-24 23:31:07 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
srv := httptest.NewServer(mux)
|
|
|
|
|
t.Cleanup(srv.Close)
|
|
|
|
|
return srv
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- scan ---
|
|
|
|
|
|
2026-03-25 09:31:38 +00:00
|
|
|
func TestScan_Scan_Good(t *testing.T) {
|
|
|
|
|
srv := mockScanServer(t)
|
|
|
|
|
s := &PrepSubsystem{
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
2026-03-30 20:37:23 +00:00
|
|
|
forge: forge.NewForge(srv.URL, "test-token"),
|
|
|
|
|
forgeURL: srv.URL,
|
|
|
|
|
forgeToken: "test-token",
|
|
|
|
|
backoff: make(map[string]time.Time),
|
|
|
|
|
failCount: make(map[string]int),
|
2026-03-25 09:31:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, out, err := s.scan(context.Background(), nil, ScanInput{Org: "core"})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.True(t, out.Success)
|
|
|
|
|
assert.Greater(t, out.Count, 0)
|
|
|
|
|
// Verify issues contain repos from mock server
|
|
|
|
|
repos := make(map[string]bool)
|
|
|
|
|
for _, iss := range out.Issues {
|
|
|
|
|
repos[iss.Repo] = true
|
|
|
|
|
}
|
|
|
|
|
assert.True(t, repos["go-io"] || repos["go-log"], "should contain issues from mock repos")
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 06:38:02 +00:00
|
|
|
func TestScan_AllRepos_Good(t *testing.T) {
|
2026-03-24 23:31:07 +00:00
|
|
|
srv := mockScanServer(t)
|
|
|
|
|
s := &PrepSubsystem{
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
2026-03-30 20:37:23 +00:00
|
|
|
forge: forge.NewForge(srv.URL, "test-token"),
|
|
|
|
|
forgeURL: srv.URL,
|
|
|
|
|
forgeToken: "test-token",
|
|
|
|
|
backoff: make(map[string]time.Time),
|
|
|
|
|
failCount: make(map[string]int),
|
2026-03-24 23:31:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, out, err := s.scan(context.Background(), nil, ScanInput{})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.True(t, out.Success)
|
|
|
|
|
assert.Greater(t, out.Count, 0)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 06:38:02 +00:00
|
|
|
func TestScan_WithLimit_Good(t *testing.T) {
|
2026-03-24 23:31:07 +00:00
|
|
|
srv := mockScanServer(t)
|
|
|
|
|
s := &PrepSubsystem{
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
2026-03-30 20:37:23 +00:00
|
|
|
forge: forge.NewForge(srv.URL, "test-token"),
|
|
|
|
|
forgeURL: srv.URL,
|
|
|
|
|
forgeToken: "test-token",
|
|
|
|
|
backoff: make(map[string]time.Time),
|
|
|
|
|
failCount: make(map[string]int),
|
2026-03-24 23:31:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, out, err := s.scan(context.Background(), nil, ScanInput{Limit: 1})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.True(t, out.Success)
|
|
|
|
|
assert.LessOrEqual(t, out.Count, 1)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 06:38:02 +00:00
|
|
|
func TestScan_DefaultLabels_Good(t *testing.T) {
|
2026-03-24 23:31:07 +00:00
|
|
|
srv := mockScanServer(t)
|
|
|
|
|
s := &PrepSubsystem{
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
2026-03-30 20:37:23 +00:00
|
|
|
forge: forge.NewForge(srv.URL, "test-token"),
|
|
|
|
|
forgeURL: srv.URL,
|
|
|
|
|
forgeToken: "test-token",
|
|
|
|
|
backoff: make(map[string]time.Time),
|
|
|
|
|
failCount: make(map[string]int),
|
2026-03-24 23:31:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Default labels: agentic, help-wanted, bug
|
|
|
|
|
_, out, err := s.scan(context.Background(), nil, ScanInput{})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.True(t, out.Success)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 06:38:02 +00:00
|
|
|
func TestScan_CustomLabels_Good(t *testing.T) {
|
2026-03-24 23:31:07 +00:00
|
|
|
srv := mockScanServer(t)
|
|
|
|
|
s := &PrepSubsystem{
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
2026-03-30 20:37:23 +00:00
|
|
|
forge: forge.NewForge(srv.URL, "test-token"),
|
|
|
|
|
forgeURL: srv.URL,
|
|
|
|
|
forgeToken: "test-token",
|
|
|
|
|
backoff: make(map[string]time.Time),
|
|
|
|
|
failCount: make(map[string]int),
|
2026-03-24 23:31:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, out, err := s.scan(context.Background(), nil, ScanInput{
|
|
|
|
|
Labels: []string{"bug"},
|
|
|
|
|
})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.True(t, out.Success)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 06:38:02 +00:00
|
|
|
func TestScan_Deduplicates_Good(t *testing.T) {
|
2026-03-24 23:31:07 +00:00
|
|
|
srv := mockScanServer(t)
|
|
|
|
|
s := &PrepSubsystem{
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
2026-03-30 20:37:23 +00:00
|
|
|
forge: forge.NewForge(srv.URL, "test-token"),
|
|
|
|
|
forgeURL: srv.URL,
|
|
|
|
|
forgeToken: "test-token",
|
|
|
|
|
backoff: make(map[string]time.Time),
|
|
|
|
|
failCount: make(map[string]int),
|
2026-03-24 23:31:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Two labels that return the same issues — should be deduped
|
|
|
|
|
_, out, err := s.scan(context.Background(), nil, ScanInput{
|
|
|
|
|
Labels: []string{"agentic", "help-wanted"},
|
|
|
|
|
Limit: 50,
|
|
|
|
|
})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.True(t, out.Success)
|
|
|
|
|
|
|
|
|
|
// Check no duplicates (same repo+number)
|
|
|
|
|
seen := make(map[string]bool)
|
|
|
|
|
for _, issue := range out.Issues {
|
|
|
|
|
key := issue.Repo + "#" + itoa(issue.Number)
|
|
|
|
|
assert.False(t, seen[key], "duplicate issue: %s", key)
|
|
|
|
|
seen[key] = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 06:38:02 +00:00
|
|
|
func TestScan_NoToken_Bad(t *testing.T) {
|
2026-03-24 23:31:07 +00:00
|
|
|
s := &PrepSubsystem{
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
2026-03-30 20:37:23 +00:00
|
|
|
forgeToken: "",
|
|
|
|
|
backoff: make(map[string]time.Time),
|
|
|
|
|
failCount: make(map[string]int),
|
2026-03-24 23:31:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, _, err := s.scan(context.Background(), nil, ScanInput{})
|
|
|
|
|
assert.Error(t, err)
|
|
|
|
|
assert.Contains(t, err.Error(), "no Forge token")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- listRepoIssues ---
|
|
|
|
|
|
2026-03-25 08:32:08 +00:00
|
|
|
func TestScan_ListRepoIssues_Good_ReturnsIssues(t *testing.T) {
|
2026-03-24 23:31:07 +00:00
|
|
|
srv := mockScanServer(t)
|
|
|
|
|
s := &PrepSubsystem{
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
2026-03-30 20:37:23 +00:00
|
|
|
forgeURL: srv.URL,
|
|
|
|
|
forgeToken: "test-token",
|
|
|
|
|
backoff: make(map[string]time.Time),
|
|
|
|
|
failCount: make(map[string]int),
|
2026-03-24 23:31:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
issues, err := s.listRepoIssues(context.Background(), "core", "go-io", "agentic")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.Len(t, issues, 2)
|
|
|
|
|
assert.Equal(t, "go-io", issues[0].Repo)
|
|
|
|
|
assert.Equal(t, 10, issues[0].Number)
|
|
|
|
|
assert.Contains(t, issues[0].Labels, "agentic")
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 08:32:08 +00:00
|
|
|
func TestScan_ListRepoIssues_Good_EmptyResult(t *testing.T) {
|
2026-03-24 23:31:07 +00:00
|
|
|
srv := mockScanServer(t)
|
|
|
|
|
s := &PrepSubsystem{
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
2026-03-30 20:37:23 +00:00
|
|
|
forgeURL: srv.URL,
|
|
|
|
|
forgeToken: "test-token",
|
|
|
|
|
backoff: make(map[string]time.Time),
|
|
|
|
|
failCount: make(map[string]int),
|
2026-03-24 23:31:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
issues, err := s.listRepoIssues(context.Background(), "core", "agent", "agentic")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.Empty(t, issues)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 08:32:08 +00:00
|
|
|
func TestScan_ListRepoIssues_Good_AssigneeExtracted(t *testing.T) {
|
2026-03-24 23:31:07 +00:00
|
|
|
srv := mockScanServer(t)
|
|
|
|
|
s := &PrepSubsystem{
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
2026-03-30 20:37:23 +00:00
|
|
|
forgeURL: srv.URL,
|
|
|
|
|
forgeToken: "test-token",
|
|
|
|
|
backoff: make(map[string]time.Time),
|
|
|
|
|
failCount: make(map[string]int),
|
2026-03-24 23:31:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
issues, err := s.listRepoIssues(context.Background(), "core", "go-io", "agentic")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.Len(t, issues, 2)
|
|
|
|
|
assert.Equal(t, "", issues[0].Assignee)
|
|
|
|
|
assert.Equal(t, "virgil", issues[1].Assignee)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-30 20:37:23 +00:00
|
|
|
func TestScan_ListRepoIssues_Good_EncodesLabelQuery(t *testing.T) {
|
|
|
|
|
expectedLabel := "bug+urgent & review"
|
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
assert.Equal(t, "/api/v1/repos/core/go-io/issues", r.URL.Path)
|
|
|
|
|
assert.Equal(t, expectedLabel, r.URL.Query().Get("labels"))
|
|
|
|
|
w.Write([]byte(core.JSONMarshalString([]map[string]any{})))
|
|
|
|
|
}))
|
|
|
|
|
t.Cleanup(srv.Close)
|
|
|
|
|
|
|
|
|
|
s := &PrepSubsystem{
|
|
|
|
|
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
|
|
|
|
forgeURL: srv.URL,
|
|
|
|
|
forgeToken: "test-token",
|
|
|
|
|
backoff: make(map[string]time.Time),
|
|
|
|
|
failCount: make(map[string]int),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
issues, err := s.listRepoIssues(context.Background(), "core", "go-io", expectedLabel)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.Empty(t, issues)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 08:32:08 +00:00
|
|
|
func TestScan_ListRepoIssues_Bad_ServerError(t *testing.T) {
|
2026-03-24 23:31:07 +00:00
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
w.WriteHeader(500)
|
|
|
|
|
}))
|
|
|
|
|
t.Cleanup(srv.Close)
|
|
|
|
|
|
|
|
|
|
s := &PrepSubsystem{
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
2026-03-30 20:37:23 +00:00
|
|
|
forgeURL: srv.URL,
|
|
|
|
|
forgeToken: "test-token",
|
|
|
|
|
backoff: make(map[string]time.Time),
|
|
|
|
|
failCount: make(map[string]int),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := s.listRepoIssues(context.Background(), "core", "go-io", "agentic")
|
|
|
|
|
assert.Error(t, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestScan_ListRepoIssues_Bad_InvalidJSON(t *testing.T) {
|
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
w.Write([]byte("not json"))
|
|
|
|
|
}))
|
|
|
|
|
t.Cleanup(srv.Close)
|
|
|
|
|
|
|
|
|
|
s := &PrepSubsystem{
|
|
|
|
|
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
|
|
|
|
forgeURL: srv.URL,
|
|
|
|
|
forgeToken: "test-token",
|
|
|
|
|
backoff: make(map[string]time.Time),
|
|
|
|
|
failCount: make(map[string]int),
|
2026-03-24 23:31:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := s.listRepoIssues(context.Background(), "core", "go-io", "agentic")
|
|
|
|
|
assert.Error(t, err)
|
2026-03-30 20:37:23 +00:00
|
|
|
assert.Contains(t, err.Error(), "parse issues response")
|
2026-03-24 23:31:07 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-25 09:19:05 +00:00
|
|
|
// --- scan Bad/Ugly ---
|
|
|
|
|
|
|
|
|
|
func TestScan_Scan_Bad(t *testing.T) {
|
|
|
|
|
// Forge returns error for org repos
|
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
w.WriteHeader(500)
|
|
|
|
|
}))
|
|
|
|
|
t.Cleanup(srv.Close)
|
|
|
|
|
|
|
|
|
|
s := &PrepSubsystem{
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
2026-03-30 20:37:23 +00:00
|
|
|
forge: forge.NewForge(srv.URL, "test-token"),
|
|
|
|
|
forgeURL: srv.URL,
|
|
|
|
|
forgeToken: "test-token",
|
|
|
|
|
backoff: make(map[string]time.Time),
|
|
|
|
|
failCount: make(map[string]int),
|
2026-03-25 09:19:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, _, err := s.scan(context.Background(), nil, ScanInput{})
|
|
|
|
|
assert.Error(t, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestScan_Scan_Ugly(t *testing.T) {
|
|
|
|
|
// Org with no repos
|
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if strings.Contains(r.URL.Path, "/orgs/") {
|
2026-03-26 06:38:02 +00:00
|
|
|
w.Write([]byte(core.JSONMarshalString([]map[string]any{})))
|
2026-03-25 09:19:05 +00:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(404)
|
|
|
|
|
}))
|
|
|
|
|
t.Cleanup(srv.Close)
|
|
|
|
|
|
|
|
|
|
s := &PrepSubsystem{
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
2026-03-30 20:37:23 +00:00
|
|
|
forge: forge.NewForge(srv.URL, "test-token"),
|
|
|
|
|
forgeURL: srv.URL,
|
|
|
|
|
forgeToken: "test-token",
|
|
|
|
|
backoff: make(map[string]time.Time),
|
|
|
|
|
failCount: make(map[string]int),
|
2026-03-25 09:19:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, out, err := s.scan(context.Background(), nil, ScanInput{})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.True(t, out.Success)
|
|
|
|
|
assert.Equal(t, 0, out.Count)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- listOrgRepos Good/Bad/Ugly ---
|
|
|
|
|
|
|
|
|
|
func TestScan_ListOrgRepos_Good(t *testing.T) {
|
|
|
|
|
srv := mockScanServer(t)
|
|
|
|
|
s := &PrepSubsystem{
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
2026-03-30 20:37:23 +00:00
|
|
|
forge: forge.NewForge(srv.URL, "test-token"),
|
|
|
|
|
forgeURL: srv.URL,
|
|
|
|
|
forgeToken: "test-token",
|
|
|
|
|
backoff: make(map[string]time.Time),
|
|
|
|
|
failCount: make(map[string]int),
|
2026-03-25 09:19:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
repos, err := s.listOrgRepos(context.Background(), "core")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.Len(t, repos, 3)
|
|
|
|
|
assert.Contains(t, repos, "go-io")
|
|
|
|
|
assert.Contains(t, repos, "go-log")
|
|
|
|
|
assert.Contains(t, repos, "agent")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestScan_ListOrgRepos_Bad(t *testing.T) {
|
|
|
|
|
// Forge returns error
|
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
w.WriteHeader(500)
|
|
|
|
|
}))
|
|
|
|
|
t.Cleanup(srv.Close)
|
|
|
|
|
|
|
|
|
|
s := &PrepSubsystem{
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
2026-03-30 20:37:23 +00:00
|
|
|
forge: forge.NewForge(srv.URL, "test-token"),
|
|
|
|
|
forgeURL: srv.URL,
|
|
|
|
|
forgeToken: "test-token",
|
|
|
|
|
backoff: make(map[string]time.Time),
|
|
|
|
|
failCount: make(map[string]int),
|
2026-03-25 09:19:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := s.listOrgRepos(context.Background(), "core")
|
|
|
|
|
assert.Error(t, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestScan_ListOrgRepos_Ugly(t *testing.T) {
|
|
|
|
|
// Empty org name
|
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
2026-03-26 06:38:02 +00:00
|
|
|
w.Write([]byte(core.JSONMarshalString([]map[string]any{})))
|
2026-03-25 09:19:05 +00:00
|
|
|
}))
|
|
|
|
|
t.Cleanup(srv.Close)
|
|
|
|
|
|
|
|
|
|
s := &PrepSubsystem{
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
2026-03-30 20:37:23 +00:00
|
|
|
forge: forge.NewForge(srv.URL, "test-token"),
|
|
|
|
|
forgeURL: srv.URL,
|
|
|
|
|
forgeToken: "test-token",
|
|
|
|
|
backoff: make(map[string]time.Time),
|
|
|
|
|
failCount: make(map[string]int),
|
2026-03-25 09:19:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
repos, err := s.listOrgRepos(context.Background(), "")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.Empty(t, repos)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- listRepoIssues Ugly ---
|
|
|
|
|
|
|
|
|
|
func TestScan_ListRepoIssues_Ugly(t *testing.T) {
|
|
|
|
|
// Issues with very long titles
|
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
longTitle := strings.Repeat("Very Long Issue Title ", 50)
|
2026-03-26 06:38:02 +00:00
|
|
|
w.Write([]byte(core.JSONMarshalString([]map[string]any{
|
2026-03-25 09:19:05 +00:00
|
|
|
{
|
|
|
|
|
"number": 1,
|
|
|
|
|
"title": longTitle,
|
|
|
|
|
"labels": []map[string]any{{"name": "agentic"}},
|
|
|
|
|
"assignee": nil,
|
|
|
|
|
"html_url": "https://forge.lthn.ai/core/go-io/issues/1",
|
|
|
|
|
},
|
2026-03-26 06:38:02 +00:00
|
|
|
})))
|
2026-03-25 09:19:05 +00:00
|
|
|
}))
|
|
|
|
|
t.Cleanup(srv.Close)
|
|
|
|
|
|
|
|
|
|
s := &PrepSubsystem{
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
2026-03-30 20:37:23 +00:00
|
|
|
forgeURL: srv.URL,
|
|
|
|
|
forgeToken: "test-token",
|
|
|
|
|
backoff: make(map[string]time.Time),
|
|
|
|
|
failCount: make(map[string]int),
|
2026-03-25 09:19:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
issues, err := s.listRepoIssues(context.Background(), "core", "go-io", "agentic")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.Len(t, issues, 1)
|
|
|
|
|
assert.True(t, len(issues[0].Title) > 100)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 08:32:08 +00:00
|
|
|
func TestScan_ListRepoIssues_Good_URLRewrite(t *testing.T) {
|
2026-03-24 23:31:07 +00:00
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
2026-03-26 06:38:02 +00:00
|
|
|
w.Write([]byte(core.JSONMarshalString([]map[string]any{
|
2026-03-24 23:31:07 +00:00
|
|
|
{
|
|
|
|
|
"number": 1,
|
|
|
|
|
"title": "Test",
|
|
|
|
|
"labels": []map[string]any{},
|
|
|
|
|
"assignee": nil,
|
|
|
|
|
"html_url": "https://forge.lthn.ai/core/go-io/issues/1",
|
|
|
|
|
},
|
2026-03-26 06:38:02 +00:00
|
|
|
})))
|
2026-03-24 23:31:07 +00:00
|
|
|
}))
|
|
|
|
|
t.Cleanup(srv.Close)
|
|
|
|
|
|
|
|
|
|
s := &PrepSubsystem{
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
|
2026-03-30 20:37:23 +00:00
|
|
|
forgeURL: srv.URL,
|
|
|
|
|
forgeToken: "test-token",
|
|
|
|
|
backoff: make(map[string]time.Time),
|
|
|
|
|
failCount: make(map[string]int),
|
2026-03-24 23:31:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
issues, err := s.listRepoIssues(context.Background(), "core", "go-io", "")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.Len(t, issues, 1)
|
|
|
|
|
// URL should be rewritten to use the mock server URL
|
|
|
|
|
assert.Contains(t, issues[0].URL, srv.URL)
|
|
|
|
|
}
|