detached f2f0e1b

refactor(ports): declare every cross-layer contract and its types in ports

- use-case interfaces (RepoService, TokenService, BrowseService, EventService,
  MergeRequestService, SessionService, WebSessionService, AuthService,
  PushService, PushHookService, SigningKeyService, RepoLookup, HookTokens,
  FailureLimiter, MaintenanceScheduler, Revalidator) move from rpc, githttp,
  web and app into ports; duplicates merge into one
- their params, pages and results move from app to ports; CreateParams and
  UpdateParams become CreateRepoParams and UpdateRepoParams, app.CommitPage
  becomes HistoryPage
- adapters no longer import app; app asserts it implements the ports
- all mocks are generated in internal/ports/mocks
commit
f2f0e1bf6ef9344c8f07974cf655c13a1f5e37f4
author
brice · · 2026-10-12 04:42 UTC
parent
ffb5f8d
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
91 files · +2065 -2439
M .hexcheck.yaml +4 -2
@@ -40,6 +40,9 @@ components:
4040 role: ignore
4141 paths:
4242 - internal/readertest/**
43+# Cross-layer contracts live in ports: adapters and app import ports and domain,
44+# never each other. hexcheck has no rule for an adapter importing app (only the
45+# reverse, no-infra-imports-in-usecase), so that direction is kept by review.
4346 rules:
4447 no-adapter-imports-in-core: error
4548 no-infra-imports-in-usecase: error
@@ -53,8 +56,7 @@ rules:
5356 mocking:
5457 generatedMockPaths:
5558 - internal/ports/mocks/**
56- - internal/app/mocks/**
57- - internal/adapters/*/mocks/**
59+ - internal/adapters/web/mocks/**
5860 generatedMockNamePatterns:
5961 - '{{Interface}}'
6062 allow:
M .mockery.yml +3 -10
@@ -1,5 +1,6 @@
1-# Mocks of the ports and of the consumer-side interfaces of the adapters and
2-# the use cases. Regenerate with: go generate ./internal/ports
1+# Mocks of the ports, generated in internal/ports/mocks, and of the generated
2+# Connect clients the web adapter calls. Regenerate with:
3+# go generate ./internal/ports
34 template: testify
45 _anchors:
56 mocks: &mocks
@@ -11,14 +12,6 @@ _anchors:
1112 packages:
1213 bnema.dev/ermite/internal/ports:
1314 config: *mocks
14- bnema.dev/ermite/internal/adapters/githttp:
15- config: *mocks
16- bnema.dev/ermite/internal/adapters/rpc:
17- config: *mocks
18- bnema.dev/ermite/internal/app:
19- config: *mocks
20- bnema.dev/ermite/internal/adapters/web:
21- config: *mocks
2215 bnema.dev/ermite-api/gen/ermite/ermiteconnect:
2316 config:
2417 include-auto-generated: true
M cmd/ermite/api_helpers_test.go +2 -2
@@ -19,8 +19,8 @@ import (
1919
2020 pb "bnema.dev/ermite-api/gen/ermite"
2121 "bnema.dev/ermite-api/gen/ermite/ermiteconnect"
22- "bnema.dev/ermite/internal/app"
2322 "bnema.dev/ermite/internal/domain"
23+ "bnema.dev/ermite/internal/ports"
2424 )
2525
2626 // clients are the generated Connect clients of one caller.
@@ -105,7 +105,7 @@ func codeName(err error) string {
105105 // commit per file to its main branch with git.
106106 func (h *harness) seed(name string, vis domain.Visibility, files ...string) {
107107 h.t.Helper()
108- _, err := h.forge.Repos.Create(h.ctx, domain.Root(), app.CreateParams{Name: h.name(name), Visibility: vis})
108+ _, err := h.forge.Repos.Create(h.ctx, domain.Root(), ports.CreateRepoParams{Name: h.name(name), Visibility: vis})
109109 require.NoError(h.t, err)
110110 if len(files) == 0 {
111111 return
M cmd/ermite/api_test.go +3 -3
@@ -24,9 +24,9 @@ import (
2424
2525 pb "bnema.dev/ermite-api/gen/ermite"
2626 "bnema.dev/ermite/internal/adapters/rpc"
27- "bnema.dev/ermite/internal/app"
2827 "bnema.dev/ermite/internal/clientlimit"
2928 "bnema.dev/ermite/internal/domain"
29+ "bnema.dev/ermite/internal/ports"
3030 )
3131
3232 // principals of the access tables.
@@ -652,8 +652,8 @@ func TestGetBlobStreamsInChunks(t *testing.T) {
652652 }
653653 }
654654
655-func createParams(h *harness, name string, vis domain.Visibility) app.CreateParams {
656- return app.CreateParams{Name: h.name(name), Visibility: vis}
655+func createParams(h *harness, name string, vis domain.Visibility) ports.CreateRepoParams {
656+ return ports.CreateRepoParams{Name: h.name(name), Visibility: vis}
657657 }
658658
659659 // TestUpdateRepoDefaultBranch: HEAD moves with git symbolic-ref once the
M cmd/ermite/e2e_helpers_test.go +2 -2
@@ -18,9 +18,9 @@ import (
1818 "github.com/bnema/zerowrap"
1919 "github.com/stretchr/testify/require"
2020
21- "bnema.dev/ermite/internal/app"
2221 "bnema.dev/ermite/internal/config"
2322 "bnema.dev/ermite/internal/domain"
23+ "bnema.dev/ermite/internal/ports"
2424 )
2525
2626 // harness is a forge wired like `ermite serve`, behind an httptest server,
@@ -186,7 +186,7 @@ func (h *harness) status(method, path string, hdr http.Header) int {
186186 // token creates a stored token through the use case, as root.
187187 func (h *harness) token(scopes []domain.Scope, repos ...string) string {
188188 h.t.Helper()
189- p := app.CreateTokenParams{Label: "e2e", Scopes: scopes}
189+ p := ports.CreateTokenParams{Label: "e2e", Scopes: scopes}
190190 for _, r := range repos {
191191 p.Repos = append(p.Repos, h.name(r))
192192 }
M cmd/ermite/e2e_test.go +8 -8
@@ -20,8 +20,8 @@ import (
2020 "github.com/stretchr/testify/assert"
2121 "github.com/stretchr/testify/require"
2222
23- "bnema.dev/ermite/internal/app"
2423 "bnema.dev/ermite/internal/domain"
24+ "bnema.dev/ermite/internal/ports"
2525 )
2626
2727 func scopes(s ...domain.Scope) []domain.Scope { return s }
@@ -29,7 +29,7 @@ func scopes(s ...domain.Scope) []domain.Scope { return s }
2929 // publicRepo creates a public repository with one commit pushed by root.
3030 func (h *harness) publicRepo(name string, files ...string) {
3131 h.t.Helper()
32- _, err := h.forge.Repos.Create(h.ctx, domain.Root(), app.CreateParams{Name: h.name(name), Visibility: domain.VisibilityPublic})
32+ _, err := h.forge.Repos.Create(h.ctx, domain.Root(), ports.CreateRepoParams{Name: h.name(name), Visibility: domain.VisibilityPublic})
3333 require.NoError(h.t, err)
3434 wt := h.workTree("main", files...)
3535 h.mustGit(wt, "push", "-q", h.urlAuth(adminToken, "/"+name+".git"), "main")
@@ -189,7 +189,7 @@ func TestAC2PushAuthorization(t *testing.T) {
189189 assert.Contains(t, out, "Authentication failed")
190190
191191 // Revoked token.
192- created, err := h.forge.Tokens.Create(h.ctx, domain.Root(), app.CreateTokenParams{Label: "short", Scopes: scopes(domain.ScopeRepoWrite)})
192+ created, err := h.forge.Tokens.Create(h.ctx, domain.Root(), ports.CreateTokenParams{Label: "short", Scopes: scopes(domain.ScopeRepoWrite)})
193193 require.NoError(t, err)
194194 next("before-revoke")
195195 out, err = h.git(wt, "push", "-q", h.urlAuth(created.Secret, "/proj.git"), "main")
@@ -570,20 +570,20 @@ func TestAC8TokenCreationThroughTheForge(t *testing.T) {
570570 h := newHarness(t, harnessOpts{})
571571 ctx := context.Background()
572572
573- admin, err := h.forge.Tokens.Create(ctx, domain.Root(), app.CreateTokenParams{
573+ admin, err := h.forge.Tokens.Create(ctx, domain.Root(), ports.CreateTokenParams{
574574 Label: "limited admin", Scopes: scopes(domain.ScopeTokenAdmin, domain.ScopeRepoWrite), Repos: []domain.RepoName{h.name("a")},
575575 })
576576 require.NoError(t, err)
577577 p, err := h.forge.Auth.Authenticate(ctx, admin.Secret)
578578 require.NoError(t, err)
579579
580- _, err = h.forge.Tokens.Create(ctx, p, app.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoAdmin), Repos: []domain.RepoName{h.name("a")}})
580+ _, err = h.forge.Tokens.Create(ctx, p, ports.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoAdmin), Repos: []domain.RepoName{h.name("a")}})
581581 assert.ErrorIs(t, err, domain.ErrForbidden, "scope escalation")
582- _, err = h.forge.Tokens.Create(ctx, p, app.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoWrite)})
582+ _, err = h.forge.Tokens.Create(ctx, p, ports.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoWrite)})
583583 assert.ErrorIs(t, err, domain.ErrForbidden, "repository list escalation")
584- _, err = h.forge.Tokens.Create(ctx, p, app.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoWrite), Repos: []domain.RepoName{h.name("b")}})
584+ _, err = h.forge.Tokens.Create(ctx, p, ports.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoWrite), Repos: []domain.RepoName{h.name("b")}})
585585 assert.ErrorIs(t, err, domain.ErrForbidden, "another repository")
586- child, err := h.forge.Tokens.Create(ctx, p, app.CreateTokenParams{Label: "child", Scopes: scopes(domain.ScopeRepoWrite), Repos: []domain.RepoName{h.name("a")}})
586+ child, err := h.forge.Tokens.Create(ctx, p, ports.CreateTokenParams{Label: "child", Scopes: scopes(domain.ScopeRepoWrite), Repos: []domain.RepoName{h.name("a")}})
587587 require.NoError(t, err)
588588
589589 // The child works on its repository, and revoking the index entry kills it at once.
M cmd/ermite/events_test.go +4 -3
@@ -21,6 +21,7 @@ import (
2121 "bnema.dev/ermite/internal/adapters/rpc"
2222 "bnema.dev/ermite/internal/app"
2323 "bnema.dev/ermite/internal/domain"
24+ "bnema.dev/ermite/internal/ports"
2425 )
2526
2627 // watchWait is how long a test waits for an event that must arrive. Only
@@ -222,7 +223,7 @@ func TestAC5NoPrivateEventsToAnonymous(t *testing.T) {
222223 assert.Equal(t, "pub", ev.GetPushed().GetRepo(), name)
223224 }
224225 // Nothing about priv came before the sentinel, for either of them.
225- _, err = h.forge.Repos.Create(h.ctx, domain.Root(), app.CreateParams{Name: h.name("sentinel"), Visibility: domain.VisibilityPublic})
226+ _, err = h.forge.Repos.Create(h.ctx, domain.Root(), ports.CreateRepoParams{Name: h.name("sentinel"), Visibility: domain.VisibilityPublic})
226227 require.NoError(t, err)
227228 for name, w := range map[string]*stream{"anonymous": anon, "scoped": scoped} {
228229 assert.Equal(t, "sentinel", w.next(t, watchWait).GetRepoCreated().GetRepo().GetName(), name)
@@ -340,7 +341,7 @@ func TestServeShutdownEndsWatchStreams(t *testing.T) {
340341 func TestWatchEndsWhenTheTokenIsRevoked(t *testing.T) {
341342 h := newHarness(t, harnessOpts{revalidateEvery: 20 * time.Millisecond})
342343 h.seed("priv", domain.VisibilityPrivate)
343- created, err := h.forge.Tokens.Create(h.ctx, domain.Root(), app.CreateTokenParams{
344+ created, err := h.forge.Tokens.Create(h.ctx, domain.Root(), ports.CreateTokenParams{
344345 Label: "watcher", Scopes: scopes(domain.ScopeRepoRead),
345346 })
346347 require.NoError(t, err)
@@ -396,7 +397,7 @@ func TestWatchLimits(t *testing.T) {
396397 // order they were published.
397398 func (h *harness) sentinel(t *testing.T, s *stream, name string) {
398399 t.Helper()
399- _, err := h.forge.Repos.Create(h.ctx, domain.Root(), app.CreateParams{Name: h.name(name), Visibility: domain.VisibilityPublic})
400+ _, err := h.forge.Repos.Create(h.ctx, domain.Root(), ports.CreateRepoParams{Name: h.name(name), Visibility: domain.VisibilityPublic})
400401 require.NoError(t, err)
401402 ev := s.next(t, watchWait)
402403 assert.Equal(t, name, ev.GetRepoCreated().GetRepo().GetName(), "an event came before the sentinel: %v", ev)
M cmd/ermite/gitreader_test.go +2 -2
@@ -10,9 +10,9 @@ import (
1010 "github.com/stretchr/testify/assert"
1111 "github.com/stretchr/testify/require"
1212
13- "bnema.dev/ermite/internal/app"
1413 "bnema.dev/ermite/internal/config"
1514 "bnema.dev/ermite/internal/domain"
15+ "bnema.dev/ermite/internal/ports"
1616 )
1717
1818 // The suite runs with the reader of ERMITE_GIT_READER (ermite-git by default).
@@ -28,7 +28,7 @@ func TestGitReaderOfTheForgeAndReadAfterPush(t *testing.T) {
2828 assert.Equal(t, want, h.forge.cfg.GitReader)
2929 assert.Equal(t, want == config.GitReaderErmiteGit, h.forge.reader != nil, "the ermite-git adapter exists only for ermite-git")
3030
31- _, err := h.forge.Repos.Create(h.ctx, domain.Root(), app.CreateParams{
31+ _, err := h.forge.Repos.Create(h.ctx, domain.Root(), ports.CreateRepoParams{
3232 Name: h.name("fresh"), Visibility: domain.VisibilityPublic,
3333 })
3434 require.NoError(t, err)
M cmd/ermite/web.go +8 -5
@@ -21,6 +21,7 @@ import (
2121 "bnema.dev/ermite/internal/clientlimit"
2222 "bnema.dev/ermite/internal/config"
2323 "bnema.dev/ermite/internal/domain"
24+ "bnema.dev/ermite/internal/ports"
2425 )
2526
2627 // newWeb builds the web pages on the API of this process: the generated
@@ -96,15 +97,17 @@ func warnSharedClientKey(ctx context.Context, trusted []netip.Prefix, maxPerClie
9697 "requests and one budget of failed authentications; set it to the proxy networks")
9798 }
9899
99-// webSessions adapts the session use cases to web.SessionAuth: a valid token
100-// gives the context of the owner, whose principal the API calls of the page
101-// carry (rpc.API.InProcessClient).
100+// webSessions adapts the session use cases to ports.WebSessionService: a valid
101+// token gives the context of the owner, whose principal the API calls of the
102+// page carry (rpc.API.InProcessClient).
102103 type webSessions struct{ *app.Sessions }
103104
105+var _ ports.WebSessionService = webSessions{}
106+
104107 func (s webSessions) Login(ctx context.Context, nonce string) (string, time.Duration, error) {
105108 token, expiresAt, err := s.Sessions.Login(ctx, nonce)
106109 if errors.Is(err, domain.ErrUnauthenticated) {
107- return "", 0, web.ErrLinkRejected
110+ return "", 0, ports.ErrLinkRejected
108111 }
109112 if err != nil {
110113 return "", 0, err
@@ -118,7 +121,7 @@ func (s webSessions) Login(ctx context.Context, nonce string) (string, time.Dura
118121 lifetime = d.Round(time.Second)
119122 }
120123 if lifetime < time.Second {
121- return "", 0, web.ErrLinkRejected
124+ return "", 0, ports.ErrLinkRejected
122125 }
123126 return token, lifetime, nil
124127 }
M cmd/ermite/web_commits_test.go +2 -2
@@ -14,8 +14,8 @@ import (
1414 "github.com/stretchr/testify/assert"
1515 "github.com/stretchr/testify/require"
1616
17- "bnema.dev/ermite/internal/app"
1817 "bnema.dev/ermite/internal/domain"
18+ "bnema.dev/ermite/internal/ports"
1919 )
2020
2121 // diffFixture is the repository of the commit pages: one commit for each
@@ -27,7 +27,7 @@ type diffFixture struct {
2727 // createRepo creates a public repository for the pages.
2828 func (h *harness) createRepo(name string, vis domain.Visibility) {
2929 h.t.Helper()
30- _, err := h.forge.Repos.Create(h.ctx, domain.Root(), app.CreateParams{Name: h.name(name), Description: "Diffs", Visibility: vis})
30+ _, err := h.forge.Repos.Create(h.ctx, domain.Root(), ports.CreateRepoParams{Name: h.name(name), Description: "Diffs", Visibility: vis})
3131 require.NoError(h.t, err)
3232 }
3333
M cmd/ermite/web_events_test.go +5 -4
@@ -17,6 +17,7 @@ import (
1717
1818 "bnema.dev/ermite/internal/app"
1919 "bnema.dev/ermite/internal/domain"
20+ "bnema.dev/ermite/internal/ports"
2021 )
2122
2223 // sseEvent is one event read from /-/events.
@@ -126,7 +127,7 @@ func TestWebEventsHome(t *testing.T) {
126127 assert.Contains(t, ev.data, `href="/pub"`)
127128
128129 // A new repository is a row; so is a repository created by a push.
129- _, err := h.forge.Repos.Create(h.ctx, domain.Root(), app.CreateParams{Name: h.name("fresh"), Description: "a <b>fresh</b> one", Visibility: domain.VisibilityPublic})
130+ _, err := h.forge.Repos.Create(h.ctx, domain.Root(), ports.CreateRepoParams{Name: h.name("fresh"), Description: "a <b>fresh</b> one", Visibility: domain.VisibilityPublic})
130131 require.NoError(t, err)
131132 ev = s.next(t, watchWait)
132133 assert.Equal(t, "home-list", ev.name)
@@ -136,7 +137,7 @@ func TestWebEventsHome(t *testing.T) {
136137
137138 // A repository that turns private is removed from the anonymous list.
138139 priv := domain.VisibilityPrivate
139- _, err = h.forge.Repos.Update(h.ctx, domain.Root(), h.name("fresh"), app.UpdateParams{Visibility: &priv})
140+ _, err = h.forge.Repos.Update(h.ctx, domain.Root(), h.name("fresh"), ports.UpdateRepoParams{Visibility: &priv})
140141 require.NoError(t, err)
141142 ev = s.next(t, watchWait)
142143 assert.Equal(t, "home-list", ev.name)
@@ -286,7 +287,7 @@ func TestWebEventsPrivate(t *testing.T) {
286287 wt := h.workTree("main", "a.txt")
287288 h.mustGit(wt, "push", "-q", h.urlAuth(adminToken, "/priv.git"), "main")
288289 desc := "secret change"
289- _, err := h.forge.Repos.Update(h.ctx, domain.Root(), h.name("priv"), app.UpdateParams{Description: &desc})
290+ _, err := h.forge.Repos.Update(h.ctx, domain.Root(), h.name("priv"), ports.UpdateRepoParams{Description: &desc})
290291 require.NoError(t, err)
291292 // Made public later, it does not reveal what happened while it was
292293 // private; the update that makes it public is shown.
@@ -303,7 +304,7 @@ func TestWebEventsPrivate(t *testing.T) {
303304 // public repository and requires its row to be the next event.
304305 func (h *harness) sentinelSSE(t *testing.T, s *sseStream, name string) {
305306 t.Helper()
306- _, err := h.forge.Repos.Create(h.ctx, domain.Root(), app.CreateParams{Name: h.name(name), Visibility: domain.VisibilityPublic})
307+ _, err := h.forge.Repos.Create(h.ctx, domain.Root(), ports.CreateRepoParams{Name: h.name(name), Visibility: domain.VisibilityPublic})
307308 require.NoError(t, err)
308309 ev := s.next(t, watchWait)
309310 assert.Contains(t, ev.data, `id="repo-`+name+`"`, "an event came before the sentinel: %s", ev.data)
M cmd/ermite/web_files_test.go +2 -2
@@ -16,8 +16,8 @@ import (
1616 "github.com/stretchr/testify/assert"
1717 "github.com/stretchr/testify/require"
1818
19- "bnema.dev/ermite/internal/app"
2019 "bnema.dev/ermite/internal/domain"
20+ "bnema.dev/ermite/internal/ports"
2121 )
2222
2323 // Trojan Source: a right-to-left override and an isolate around code.
@@ -37,7 +37,7 @@ func tinyPNG(t *testing.T) []byte {
3737 // tag with a slash.
3838 func (h *harness) webFilesRepo(name string) {
3939 h.t.Helper()
40- _, err := h.forge.Repos.Create(h.ctx, domain.Root(), app.CreateParams{
40+ _, err := h.forge.Repos.Create(h.ctx, domain.Root(), ports.CreateRepoParams{
4141 Name: h.name(name), Description: "Files of every kind", Visibility: domain.VisibilityPublic,
4242 })
4343 require.NoError(h.t, err)
M cmd/ermite/web_mr_test.go +2 -2
@@ -13,8 +13,8 @@ import (
1313 "github.com/stretchr/testify/assert"
1414 "github.com/stretchr/testify/require"
1515
16- "bnema.dev/ermite/internal/app"
1716 "bnema.dev/ermite/internal/domain"
17+ "bnema.dev/ermite/internal/ports"
1818 )
1919
2020 // mrRepo is a public repository "proj" with README.md on main and a branch
@@ -466,7 +466,7 @@ func TestWebMergeRequestLimitIsFriendly(t *testing.T) {
466466 token := r.signIn(t)
467467 r.cli(t, adminToken, "mr", "create", "proj", "--source", "feat/search", "--title", "t").ok(t)
468468 for i := 0; i < 1000; i++ {
469- _, err := r.forge.MergeRequests.AddComment(r.ctx, domain.Root(), r.name("proj"), 1, app.AddCommentParams{Text: "n"})
469+ _, err := r.forge.MergeRequests.AddComment(r.ctx, domain.Root(), r.name("proj"), 1, ports.AddCommentParams{Text: "n"})
470470 require.NoError(t, err)
471471 }
472472 full := b.form("/proj/merge-requests/1/comments", token, url.Values{"text": {"one more"}})
M cmd/ermite/web_session_test.go +2 -2
@@ -19,8 +19,8 @@ import (
1919 "github.com/stretchr/testify/require"
2020
2121 pb "bnema.dev/ermite-api/gen/ermite"
22- "bnema.dev/ermite/internal/app"
2322 "bnema.dev/ermite/internal/domain"
23+ "bnema.dev/ermite/internal/ports"
2424 )
2525
2626 const (
@@ -311,7 +311,7 @@ func TestWebRevokingAnOwnerTokenSignsEveryoneOut(t *testing.T) {
311311
312312 // The cap: a link made by a token that expires in an hour gives a cookie,
313313 // and a session, of an hour.
314- soon, err := c.forge.Tokens.Create(c.ctx, domain.Root(), app.CreateTokenParams{
314+ soon, err := c.forge.Tokens.Create(c.ctx, domain.Root(), ports.CreateTokenParams{
315315 Label: "soon", Scopes: domain.AllScopes(), ExpiresAt: time.Now().Add(time.Hour),
316316 })
317317 require.NoError(t, err)
M cmd/ermite/web_test.go +3 -3
@@ -16,8 +16,8 @@ import (
1616 "github.com/stretchr/testify/assert"
1717 "github.com/stretchr/testify/require"
1818
19- "bnema.dev/ermite/internal/app"
2019 "bnema.dev/ermite/internal/domain"
20+ "bnema.dev/ermite/internal/ports"
2121 )
2222
2323 var updateGolden = flag.Bool("update", false, "rewrite the golden files of the web tests")
@@ -30,7 +30,7 @@ const webFixtureReadme = "# Demo\n\nA small *demo* with a [guide](docs/guide.md)
3030 // branches (one with a slash), two tags and a description.
3131 func (h *harness) webRepo(name string) {
3232 h.t.Helper()
33- _, err := h.forge.Repos.Create(h.ctx, domain.Root(), app.CreateParams{
33+ _, err := h.forge.Repos.Create(h.ctx, domain.Root(), ports.CreateRepoParams{
3434 Name: h.name(name), Description: "A demo repository", Visibility: domain.VisibilityPublic,
3535 })
3636 require.NoError(h.t, err)
@@ -162,7 +162,7 @@ func TestWebPrivateRepoIsInvisible(t *testing.T) {
162162 // AC4, end to end: a README that attacks the page is neutralised.
163163 func TestWebReadmeIsNeutralised(t *testing.T) {
164164 h := newHarness(t, harnessOpts{})
165- _, err := h.forge.Repos.Create(h.ctx, domain.Root(), app.CreateParams{Name: h.name("evil"), Visibility: domain.VisibilityPublic})
165+ _, err := h.forge.Repos.Create(h.ctx, domain.Root(), ports.CreateRepoParams{Name: h.name("evil"), Visibility: domain.VisibilityPublic})
166166 require.NoError(t, err)
167167 wt := h.t.TempDir()
168168 h.mustGit(wt, "init", "-q", "--initial-branch=main")
M internal/adapters/githttp/handler.go +8 -44
@@ -61,56 +61,18 @@ const (
6161 serviceReceive = "git-receive-pack"
6262 )
6363
64-// Authenticator turns the secret of a request into a principal. It returns
65-// domain.ErrUnauthenticated for a secret that is not valid. app.Auth fits.
66-type Authenticator interface {
67- Authenticate(ctx context.Context, secret string) (domain.Principal, error)
68-}
69-
70-// Repos is the read side of the repository use cases. A repository the
71-// principal may not read is domain.ErrNotFound. app.Repos fits.
72-type Repos interface {
73- Get(ctx context.Context, p domain.Principal, name domain.RepoName) (domain.Repo, error)
74-}
75-
76-// Pushes opens a repository for a push: it is domain.ErrUnauthenticated for an
77-// anonymous caller, domain.ErrNotFound for a repository the caller may not
78-// write and cannot see (or that is missing and may not be created),
79-// domain.ErrForbidden for one it can see but not write. With create, a missing
80-// repository the principal may write is created. app.Pushes fits.
81-type Pushes interface {
82- Open(ctx context.Context, p domain.Principal, name domain.RepoName, create bool) error
83-}
84-
85-// HookTokens hands out the per-request tokens the hooks of a push present
86-// back to ermite, and the socket they present them on.
87-type HookTokens interface {
88- // Register binds a new token to the repository and principal of a push.
89- // release revokes it and must be called when the request ends.
90- Register(ctx context.Context, repo domain.RepoName, p domain.Principal) (token string, release func())
91- // Socket is the path of the hook socket.
92- Socket() string
93-}
94-
95-// FailureLimiter counts the failed authentications per client, with the other
96-// adapters that authenticate. *clientlimit.Limiter fits.
97-type FailureLimiter interface {
98- Blocked(ctx context.Context, addr string) (bool, time.Duration)
99- Fail(ctx context.Context, addr string)
100-}
101-
10264 // Config configures a Handler.
10365 type Config struct {
104- Auth Authenticator
105- Repos Repos
106- Pushes Pushes
66+ Auth ports.AuthService
67+ Repos ports.RepoLookup
68+ Pushes ports.PushService
10769 Backend ports.GitBackend
10870 // Hooks provides hook tokens for pushes. It is required.
109- Hooks HookTokens
71+ Hooks ports.HookTokens
11072 // TrustedProxies are the networks allowed to set X-Forwarded-For.
11173 TrustedProxies []netip.Prefix
11274 // Limiter counts failed authentications; nil builds a default one.
113- Limiter FailureLimiter
75+ Limiter ports.FailureLimiter
11476 // MaxBodyBytes bounds the body of a push; 0 means DefaultMaxBodyBytes.
11577 // The body of a fetch is bounded by 64 MiB at most.
11678 MaxBodyBytes int64
@@ -148,7 +110,7 @@ const (
148110 // Handler serves the smart HTTP routes of Patterns.
149111 type Handler struct {
150112 cfg Config
151- limiter FailureLimiter
113+ limiter ports.FailureLimiter
152114 maxBody int64
153115
154116 upload, receive chan struct{} // slots of the backends, per service
@@ -220,6 +182,7 @@ func parseRoute(r *http.Request) (route, int) {
220182 }
221183
222184 // ServeHTTP implements http.Handler.
185+//hexcheck:ignore suspicious-business-logic-in-adapter transport orchestration: the authorization decisions are the use cases'
223186 func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
224187 var rt route
225188 var name domain.RepoName
@@ -411,6 +374,7 @@ func (h *Handler) acquire(ctx context.Context, w http.ResponseWriter, rt route,
411374 }
412375
413376 // serveBackend runs git http-backend for an authorized request.
377+//hexcheck:ignore suspicious-business-logic-in-adapter process and CGI plumbing: no business rule is decided here
414378 func (h *Handler) serveBackend(w http.ResponseWriter, r *http.Request, rt route, name domain.RepoName, p domain.Principal, client string) {
415379 ctx := r.Context()
416380 logger := zerowrap.FromCtx(ctx)
M internal/adapters/githttp/handler_test.go +10 -11
@@ -21,7 +21,6 @@ import (
2121 "github.com/stretchr/testify/require"
2222
2323 "bnema.dev/ermite/internal/adapters/githttp"
24- ghmocks "bnema.dev/ermite/internal/adapters/githttp/mocks"
2524 "bnema.dev/ermite/internal/clientlimit"
2625 "bnema.dev/ermite/internal/domain"
2726 "bnema.dev/ermite/internal/ports"
@@ -43,10 +42,10 @@ func cgiScript(t *testing.T) string {
4342 type rig struct {
4443 h *githttp.Handler
4544 srv *httptest.Server
46- pushes *ghmocks.Pushes
45+ pushes *mocks.PushService
4746 backend *mocks.GitBackend
48- hooks *ghmocks.HookTokens
49- auth *ghmocks.Authenticator
47+ hooks *mocks.HookTokens
48+ auth *mocks.AuthService
5049
5150 pushFlags []bool
5251 backendErr error
@@ -93,7 +92,7 @@ func (r *rig) hookedRepo() domain.RepoName {
9392
9493 // stubUseCases answers like the use cases behind the handler: a root and a
9594 // reader token, a public repository "pub" and a private one "priv".
96-func (r *rig) stubUseCases(repos *ghmocks.Repos) {
95+func (r *rig) stubUseCases(repos *mocks.RepoLookup) {
9796 r.auth.EXPECT().Authenticate(mock.Anything, mock.Anything).RunAndReturn(
9897 func(_ context.Context, secret string) (domain.Principal, error) {
9998 r.mu.Lock()
@@ -145,10 +144,10 @@ func (r *rig) stubUseCases(repos *ghmocks.Repos) {
145144 func newRig(t *testing.T, mut func(*githttp.Config)) *rig {
146145 t.Helper()
147146 r := &rig{
148- pushes: ghmocks.NewPushes(t), backend: mocks.NewGitBackend(t),
149- hooks: ghmocks.NewHookTokens(t), auth: ghmocks.NewAuthenticator(t),
147+ pushes: mocks.NewPushService(t), backend: mocks.NewGitBackend(t),
148+ hooks: mocks.NewHookTokens(t), auth: mocks.NewAuthService(t),
150149 }
151- repos := ghmocks.NewRepos(t)
150+ repos := mocks.NewRepoLookup(t)
152151 r.stubUseCases(repos)
153152 cfg := githttp.Config{
154153 Auth: r.auth, Repos: repos, Pushes: r.pushes, Backend: r.backend, Hooks: r.hooks,
@@ -511,7 +510,7 @@ func TestHandlerUsesTheFailureLimiter(t *testing.T) {
511510 path := "/pub.git/info/refs?service=git-upload-pack"
512511 t.Run("a blocked client is refused before authentication", func(t *testing.T) {
513512 t.Parallel()
514- limiter := ghmocks.NewFailureLimiter(t)
513+ limiter := mocks.NewFailureLimiter(t)
515514 limiter.EXPECT().Blocked(mock.Anything, "127.0.0.1").Return(true, 3*time.Second).Once()
516515 r := newRig(t, func(c *githttp.Config) { c.Limiter = limiter })
517516 code, _, hdr := r.do(t, "GET", path, "u", "root-secret", nil, nil)
@@ -521,7 +520,7 @@ func TestHandlerUsesTheFailureLimiter(t *testing.T) {
521520 })
522521 t.Run("an invalid secret is recorded as a failure", func(t *testing.T) {
523522 t.Parallel()
524- limiter := ghmocks.NewFailureLimiter(t)
523+ limiter := mocks.NewFailureLimiter(t)
525524 limiter.EXPECT().Blocked(mock.Anything, "127.0.0.1").Return(false, time.Duration(0)).Once()
526525 limiter.EXPECT().Fail(mock.Anything, "127.0.0.1").Once()
527526 r := newRig(t, func(c *githttp.Config) { c.Limiter = limiter })
@@ -530,7 +529,7 @@ func TestHandlerUsesTheFailureLimiter(t *testing.T) {
530529 })
531530 t.Run("a valid secret is not a failure", func(t *testing.T) {
532531 t.Parallel()
533- limiter := ghmocks.NewFailureLimiter(t)
532+ limiter := mocks.NewFailureLimiter(t)
534533 limiter.EXPECT().Blocked(mock.Anything, "127.0.0.1").Return(false, time.Duration(0)).Once()
535534 r := newRig(t, func(c *githttp.Config) { c.Limiter = limiter })
536535 code, _, _ := r.do(t, "GET", path, "u", "root-secret", nil, nil)
M internal/adapters/githttp/hooks.go +5 -12
@@ -19,17 +19,9 @@ import (
1919 "github.com/bnema/zerowrap"
2020
2121 "bnema.dev/ermite/internal/domain"
22+ "bnema.dev/ermite/internal/ports"
2223 )
2324
24-// PushHooks is what the hook socket asks of the use cases. app.Pushes fits.
25-type PushHooks interface {
26- // Check decides a push before Git applies it; an error matching
27- // domain.ErrRejected refuses it, any other error refuses it as a failure.
28- Check(ctx context.Context, p domain.Principal, name domain.RepoName, updates []domain.RefUpdate) error
29- // Record handles an accepted push.
30- Record(ctx context.Context, p domain.Principal, name domain.RepoName, updates []domain.RefUpdate) error
31-}
32-
3325 // readTimeout bounds how long a connection may take to send its request line.
3426 const readTimeout = 30 * time.Second
3527
@@ -46,7 +38,7 @@ type hookEntry struct {
4638 // socket the hooks call back on. It implements HookTokens.
4739 type HookServer struct {
4840 path string
49- hooks PushHooks
41+ hooks ports.PushHookService
5042
5143 mu sync.RWMutex
5244 tokens map[string]hookEntry
@@ -57,10 +49,10 @@ type HookServer struct {
5749 wg sync.WaitGroup
5850 }
5951
60-var _ HookTokens = (*HookServer)(nil)
52+var _ ports.HookTokens = (*HookServer)(nil)
6153
6254 // NewHookServer builds a server for the socket at path. Call Serve to listen.
63-func NewHookServer(path string, hooks PushHooks) *HookServer {
55+func NewHookServer(path string, hooks ports.PushHookService) *HookServer {
6456 return &HookServer{path: path, hooks: hooks, tokens: make(map[string]hookEntry), conns: make(map[net.Conn]struct{})}
6557 }
6658
@@ -183,6 +175,7 @@ func (s *HookServer) Close(ctx context.Context) {
183175 }
184176
185177 // handle serves one connection: one request line, one reply line.
178+//hexcheck:ignore suspicious-business-logic-in-adapter hook protocol dispatch: the decisions are the use cases'
186179 func (s *HookServer) handle(ctx context.Context, conn net.Conn) {
187180 log := zerowrap.FromCtx(ctx)
188181 _ = conn.SetReadDeadline(time.Now().Add(readTimeout))
M internal/adapters/githttp/hooks_test.go +10 -9
@@ -22,8 +22,9 @@ import (
2222 "github.com/stretchr/testify/require"
2323
2424 "bnema.dev/ermite/internal/adapters/githttp"
25- ghmocks "bnema.dev/ermite/internal/adapters/githttp/mocks"
2625 "bnema.dev/ermite/internal/domain"
26+ "bnema.dev/ermite/internal/ports"
27+ "bnema.dev/ermite/internal/ports/mocks"
2728 )
2829
2930 const (
@@ -39,7 +40,7 @@ func repoName(t *testing.T, s string) domain.RepoName {
3940 }
4041
4142 // startHooks serves a hook socket in a short temporary directory.
42-func startHooks(t *testing.T, h githttp.PushHooks) (*githttp.HookServer, string) {
43+func startHooks(t *testing.T, h ports.PushHookService) (*githttp.HookServer, string) {
4344 t.Helper()
4445 dir, err := os.MkdirTemp("", "hk")
4546 require.NoError(t, err)
@@ -80,7 +81,7 @@ func raw(t *testing.T, sock, line string) githttp.HookReply {
8081
8182 func TestHookSocketMode(t *testing.T) {
8283 t.Parallel()
83- _, sock := startHooks(t, ghmocks.NewPushHooks(t))
84+ _, sock := startHooks(t, mocks.NewPushHookService(t))
8485 fi, err := os.Stat(sock)
8586 require.NoError(t, err)
8687 assert.Equal(t, os.FileMode(0o600), fi.Mode().Perm())
@@ -94,21 +95,21 @@ func TestHookServerReplacesStaleSocketFile(t *testing.T) {
9495 defer func() { _ = os.RemoveAll(dir) }()
9596 sock := filepath.Join(dir, "hook.sock")
9697 require.NoError(t, os.WriteFile(sock, []byte("stale"), 0o600))
97- s := githttp.NewHookServer(sock, ghmocks.NewPushHooks(t))
98+ s := githttp.NewHookServer(sock, mocks.NewPushHookService(t))
9899 require.NoError(t, s.Listen(context.Background()))
99100 s.Close(context.Background())
100101 }
101102
102103 func TestHookServerRejectsTooLongSocketPath(t *testing.T) {
103104 t.Parallel()
104- s := githttp.NewHookServer("/"+strings.Repeat("a", 120)+"/hook.sock", ghmocks.NewPushHooks(t))
105+ s := githttp.NewHookServer("/"+strings.Repeat("a", 120)+"/hook.sock", mocks.NewPushHookService(t))
105106 err := s.Listen(context.Background())
106107 require.Error(t, err)
107108 assert.Contains(t, err.Error(), "too long")
108109 }
109110
110111 func TestHookProtocol(t *testing.T) {
111- hooks := ghmocks.NewPushHooks(t)
112+ hooks := mocks.NewPushHookService(t)
112113 srv, sock := startHooks(t, hooks)
113114 p := domain.TokenPrincipal(domain.Token{ID: "tok1", Scopes: []domain.Scope{domain.ScopeRepoWrite}})
114115 tokenA, releaseA := srv.Register(context.Background(), repoName(t, "alpha"), p)
@@ -202,7 +203,7 @@ func TestHookProtocol(t *testing.T) {
202203 // push is already in the repository.
203204 func TestHookRecordSurvivesShutdown(t *testing.T) {
204205 t.Parallel()
205- hooks := ghmocks.NewPushHooks(t)
206+ hooks := mocks.NewPushHookService(t)
206207 started, proceed := make(chan struct{}), make(chan struct{})
207208 var cancelled, hasDeadline bool
208209 var ctxMu sync.Mutex
@@ -257,7 +258,7 @@ func TestHookRecordSurvivesShutdown(t *testing.T) {
257258
258259 func TestHookConcurrentTokens(t *testing.T) {
259260 t.Parallel()
260- hooks := ghmocks.NewPushHooks(t)
261+ hooks := mocks.NewPushHookService(t)
261262 srv, sock := startHooks(t, hooks)
262263 var wg sync.WaitGroup
263264 // Each of the 16 tokens reaches the use case for its own repository, once.
@@ -280,7 +281,7 @@ func TestHookConcurrentTokens(t *testing.T) {
280281
281282 func TestRunHook(t *testing.T) {
282283 ctx := context.Background()
283- hooks := ghmocks.NewPushHooks(t)
284+ hooks := mocks.NewPushHookService(t)
284285 srv, sock := startHooks(t, hooks)
285286 tok, rel := srv.Register(context.Background(), repoName(t, "alpha"), domain.Root())
286287 defer rel()
M internal/adapters/rpc/api.go +10 -15
@@ -18,6 +18,7 @@ import (
1818 "bnema.dev/ermite-api/gen/ermite/ermiteconnect"
1919 "bnema.dev/ermite/internal/clientlimit"
2020 "bnema.dev/ermite/internal/domain"
21+ "bnema.dev/ermite/internal/ports"
2122 )
2223
2324 // MaxRequestBytes bounds the size of a request message. Responses are not
@@ -28,22 +29,16 @@ const MaxRequestBytes = 1 << 20
2829 // implements, as GetVersion lists them.
2930 var Features = []string{"repo.update", "events.resume", "merge_requests", "merge_requests.delete_branch", "signing_key", "session"}
3031
31-// Authenticator turns a token secret into a principal, or
32-// domain.ErrUnauthenticated. app.Auth fits.
33-type Authenticator interface {
34- Authenticate(ctx context.Context, secret string) (domain.Principal, error)
35-}
36-
3732 // Config configures the API.
3833 type Config struct {
39- Auth Authenticator
40- Repos Repos
41- Tokens Tokens
42- Browse Browse
43- Events Events
34+ Auth ports.AuthService
35+ Repos ports.RepoService
36+ Tokens ports.TokenService
37+ Browse ports.BrowseService
38+ Events ports.EventService
4439 // MergeRequests serves MergeRequestService, SigningKey GetSigningKey.
45- MergeRequests MergeRequests
46- SigningKey SigningKey
40+ MergeRequests ports.MergeRequestService
41+ SigningKey ports.SigningKeyService
4742 Version string
4843 // TrustedProxies are the networks allowed to set X-Forwarded-For.
4944 TrustedProxies []netip.Prefix
@@ -51,11 +46,11 @@ type Config struct {
5146 // it with the other adapters that authenticate, so that a client has one
5247 // budget of failures. Leave it unset rather than assigning a typed nil
5348 // pointer (a nil *T in the interface is not nil, and would be called).
54- Limiter FailureLimiter
49+ Limiter ports.FailureLimiter
5550 // Sessions are the owner's web sessions; nil serves no SessionService.
5651 // Leave it unset rather than assigning a typed nil pointer (a nil *T in
5752 // the interface is not nil, and the service would be served and called).
58- Sessions Sessions
53+ Sessions ports.SessionService
5954 }
6055
6156 // API is the set of Connect handlers of the contract.
M internal/adapters/rpc/convert.go +4 -4
@@ -9,8 +9,8 @@ import (
99 "google.golang.org/protobuf/types/known/timestamppb"
1010
1111 pb "bnema.dev/ermite-api/gen/ermite"
12- "bnema.dev/ermite/internal/app"
1312 "bnema.dev/ermite/internal/domain"
13+ "bnema.dev/ermite/internal/ports"
1414 )
1515
1616 // This file maps domain values to the messages of the contract and back.
@@ -55,7 +55,7 @@ func visibilityFromPB(v pb.Visibility) (domain.Visibility, error) {
5555 return "", fmt.Errorf("%w: %v", domain.ErrInvalidVisibility, v)
5656 }
5757
58-func repoToPB(v app.RepoView) *pb.Repo {
58+func repoToPB(v ports.RepoView) *pb.Repo {
5959 settings := v.Settings
6060 if settings.IsZero() {
6161 settings = domain.DefaultRepoSettings()
@@ -361,7 +361,7 @@ func refUpdatesToPB(updates []domain.RefUpdate) []*pb.RefUpdate {
361361
362362 // watchEventToPB maps one item of a watch; nil when the event has no
363363 // representation in the contract.
364-func watchEventToPB(we app.WatchEvent) *pb.Event {
364+func watchEventToPB(we ports.WatchEvent) *pb.Event {
365365 if we.Gap {
366366 return &pb.Event{Kind: &pb.Event_Gap{Gap: &pb.Gap{}}}
367367 }
@@ -397,7 +397,7 @@ func watchEventToPB(we app.WatchEvent) *pb.Event {
397397 return out
398398 }
399399
400-func repoOrName(we app.WatchEvent, name domain.RepoName, vis domain.Visibility) *pb.Repo {
400+func repoOrName(we ports.WatchEvent, name domain.RepoName, vis domain.Visibility) *pb.Repo {
401401 if we.Repo != nil {
402402 return repoToPB(*we.Repo)
403403 }
M internal/adapters/rpc/convert_test.go +5 -5
@@ -15,8 +15,8 @@ import (
1515 "google.golang.org/protobuf/types/known/fieldmaskpb"
1616
1717 pb "bnema.dev/ermite-api/gen/ermite"
18- "bnema.dev/ermite/internal/app"
1918 "bnema.dev/ermite/internal/domain"
19+ "bnema.dev/ermite/internal/ports"
2020 )
2121
2222 // Every value of the domain maps to a real value of the contract, never to
@@ -212,12 +212,12 @@ func TestCommentID(t *testing.T) {
212212 func TestRepoToPBAlwaysHasSettings(t *testing.T) {
213213 t.Parallel()
214214 // A repository value without settings (never stored with them) reads as the defaults.
215- r := repoToPB(app.RepoView{Repo: domain.Repo{Name: mustName(t, "x")}})
215+ r := repoToPB(ports.RepoView{Repo: domain.Repo{Name: mustName(t, "x")}})
216216 assert.Equal(t, []pb.MergeMode{pb.MergeMode_MERGE_MODE_FAST_FORWARD, pb.MergeMode_MERGE_MODE_SQUASH}, r.GetSettings().GetAllowedMergeModes())
217217 assert.True(t, r.GetSettings().GetFailingChecksBlock())
218218
219219 custom := domain.RepoSettings{AllowedMergeModes: []domain.MergeMode{domain.MergeModeSquash}}
220- r = repoToPB(app.RepoView{Repo: domain.Repo{Name: mustName(t, "x"), Settings: custom}})
220+ r = repoToPB(ports.RepoView{Repo: domain.Repo{Name: mustName(t, "x"), Settings: custom}})
221221 assert.Equal(t, []pb.MergeMode{pb.MergeMode_MERGE_MODE_SQUASH}, r.GetSettings().GetAllowedMergeModes())
222222 assert.False(t, r.GetSettings().GetFailingChecksBlock())
223223 }
@@ -313,7 +313,7 @@ func TestWatchEventToPBMergeRequestEvents(t *testing.T) {
313313 } {
314314 t.Run(name, func(t *testing.T) {
315315 t.Parallel()
316- got := watchEventToPB(app.WatchEvent{ID: "e1", Event: tc.ev})
316+ got := watchEventToPB(ports.WatchEvent{ID: "e1", Event: tc.ev})
317317 require.NotNil(t, got)
318318 assert.Equal(t, "e1", got.GetId())
319319 assert.Equal(t, at, got.GetTime().AsTime())
@@ -327,7 +327,7 @@ func TestWatchEventToPBMergeRequestEvents(t *testing.T) {
327327 }
328328
329329 c := domain.Comment{ID: 9, Author: "root", Path: "a.txt", Line: 3, Side: domain.SideOld, CreatedAt: at}
330- got := watchEventToPB(app.WatchEvent{ID: "e2", Event: domain.CommentAdded{Repo: repo, Number: 4, Comment: c, At: at}})
330+ got := watchEventToPB(ports.WatchEvent{ID: "e2", Event: domain.CommentAdded{Repo: repo, Number: 4, Comment: c, At: at}})
331331 added := got.GetCommentAdded()
332332 require.NotNil(t, added)
333333 assert.Equal(t, "proj", added.GetRepo())
M internal/adapters/rpc/event.go +4 -4
@@ -8,11 +8,11 @@ import (
88 "connectrpc.com/connect"
99
1010 pb "bnema.dev/ermite-api/gen/ermite"
11- "bnema.dev/ermite/internal/app"
11+ "bnema.dev/ermite/internal/ports"
1212 )
1313
1414 // eventService implements ermiteconnect.EventServiceHandler.
15-type eventService struct{ events Events }
15+type eventService struct{ events ports.EventService }
1616
1717 // Watch streams events until the client goes away or the server stops. The
1818 // stream ends with success in both cases: the contract has no error for
@@ -20,7 +20,7 @@ type eventService struct{ events Events }
2020 // the caller is revoked or expires, and a caller that has too many streams
2121 // open gets resource_exhausted.
2222 func (s *eventService) Watch(ctx context.Context, req *connect.Request[pb.WatchRequest], stream *connect.ServerStream[pb.WatchResponse]) error {
23- params := app.WatchParams{
23+ params := ports.WatchParams{
2424 Topics: req.Msg.GetTopics(), ResumeAfter: req.Msg.GetResumeAfter(),
2525 Client: clientFromContext(ctx),
2626 // A client's call returns when the response begins: send the headers
@@ -28,7 +28,7 @@ func (s *eventService) Watch(ctx context.Context, req *connect.Request[pb.WatchR
2828 // them for a nil message. A failure shows on the first real send.
2929 Ready: func() { _ = stream.Send(nil) },
3030 }
31- err := s.events.Watch(ctx, caller(ctx), params, func(we app.WatchEvent) error {
31+ err := s.events.Watch(ctx, caller(ctx), params, func(we ports.WatchEvent) error {
3232 ev := watchEventToPB(we)
3333 if ev == nil {
3434 return nil
M internal/adapters/rpc/git.go +4 -4
@@ -11,15 +11,15 @@ import (
1111 "connectrpc.com/connect"
1212
1313 pb "bnema.dev/ermite-api/gen/ermite"
14- "bnema.dev/ermite/internal/app"
1514 "bnema.dev/ermite/internal/domain"
15+ "bnema.dev/ermite/internal/ports"
1616 )
1717
1818 // BlobChunkSize is the largest chunk of a GetBlob stream.
1919 const BlobChunkSize = 64 << 10
2020
2121 // gitService implements ermiteconnect.GitServiceHandler.
22-type gitService struct{ browse Browse }
22+type gitService struct{ browse ports.BrowseService }
2323
2424 // target is the repository and revision most requests start with.
2525 func target(repo, revision string) (domain.RepoName, domain.Revision, error) {
@@ -39,7 +39,7 @@ func (s *gitService) ListRefs(ctx context.Context, req *connect.Request[pb.ListR
3939 if err != nil {
4040 return nil, connectError(ctx, err)
4141 }
42- params := app.ListRefsParams{PageSize: int(req.Msg.GetPageSize()), PageToken: req.Msg.GetPageToken()}
42+ params := ports.ListRefsParams{PageSize: int(req.Msg.GetPageSize()), PageToken: req.Msg.GetPageToken()}
4343 for _, k := range req.Msg.GetKinds() {
4444 kind, err := refKindFromPB(k)
4545 if err != nil {
@@ -144,7 +144,7 @@ func (s *gitService) ListCommits(ctx context.Context, req *connect.Request[pb.Li
144144 if err != nil {
145145 return nil, connectError(ctx, err)
146146 }
147- page, err := s.browse.ListCommits(ctx, caller(ctx), name, app.ListCommitsParams{
147+ page, err := s.browse.ListCommits(ctx, caller(ctx), name, ports.ListCommitsParams{
148148 Revision: rev, Path: path, PageSize: int(req.Msg.GetPageSize()), PageToken: req.Msg.GetPageToken(),
149149 })
150150 if err != nil {
M internal/adapters/rpc/git_test.go +12 -12
@@ -11,9 +11,9 @@ import (
1111 "github.com/stretchr/testify/require"
1212
1313 pb "bnema.dev/ermite-api/gen/ermite"
14- "bnema.dev/ermite/internal/adapters/rpc/mocks"
15- "bnema.dev/ermite/internal/app"
1614 "bnema.dev/ermite/internal/domain"
15+ "bnema.dev/ermite/internal/ports"
16+ "bnema.dev/ermite/internal/ports/mocks"
1717 )
1818
1919 func TestGitServiceGetTree(t *testing.T) {
@@ -34,7 +34,7 @@ func TestGitServiceGetTree(t *testing.T) {
3434 for _, tt := range tests {
3535 t.Run(tt.name, func(t *testing.T) {
3636 t.Parallel()
37- browse := mocks.NewBrowse(t)
37+ browse := mocks.NewBrowseService(t)
3838 if tt.wantCall {
3939 browse.EXPECT().GetTree(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), mock.Anything, mock.Anything, false).
4040 Return(domain.Tree{}, tt.usecase).Once()
@@ -68,10 +68,10 @@ func TestGitServiceListRefs(t *testing.T) {
6868 for _, tt := range tests {
6969 t.Run(tt.name, func(t *testing.T) {
7070 t.Parallel()
71- browse := mocks.NewBrowse(t)
71+ browse := mocks.NewBrowseService(t)
7272 if tt.wantCall {
7373 browse.EXPECT().ListRefs(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), mock.Anything).
74- Return(app.RefPage{NextPageToken: "n"}, tt.usecase).Once()
74+ Return(ports.RefPage{NextPageToken: "n"}, tt.usecase).Once()
7575 }
7676 resp, err := (&gitService{browse}).ListRefs(ownerCtx(), connect.NewRequest(tt.req))
7777 if tt.wantCode != 0 {
@@ -93,26 +93,26 @@ func TestGitServiceGetCommitAndResolveRevision(t *testing.T) {
9393 for _, tt := range invalid {
9494 t.Run("GetCommit "+tt.name, func(t *testing.T) {
9595 t.Parallel()
96- _, err := (&gitService{mocks.NewBrowse(t)}).GetCommit(ownerCtx(), connect.NewRequest(&pb.GetCommitRequest{Repo: tt.repo, Revision: tt.rev}))
96+ _, err := (&gitService{mocks.NewBrowseService(t)}).GetCommit(ownerCtx(), connect.NewRequest(&pb.GetCommitRequest{Repo: tt.repo, Revision: tt.rev}))
9797 requireCode(t, connect.CodeInvalidArgument, err)
9898 })
9999 t.Run("ResolveRevision "+tt.name, func(t *testing.T) {
100100 t.Parallel()
101- _, err := (&gitService{mocks.NewBrowse(t)}).ResolveRevision(ownerCtx(), connect.NewRequest(&pb.ResolveRevisionRequest{Repo: tt.repo, Revision: tt.rev}))
101+ _, err := (&gitService{mocks.NewBrowseService(t)}).ResolveRevision(ownerCtx(), connect.NewRequest(&pb.ResolveRevisionRequest{Repo: tt.repo, Revision: tt.rev}))
102102 requireCode(t, connect.CodeInvalidArgument, err)
103103 })
104104 }
105105
106106 t.Run("GetCommit not found", func(t *testing.T) {
107107 t.Parallel()
108- browse := mocks.NewBrowse(t)
108+ browse := mocks.NewBrowseService(t)
109109 browse.EXPECT().GetCommit(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), mock.Anything).Return(domain.CommitDetail{}, domain.ErrNotFound).Once()
110110 _, err := (&gitService{browse}).GetCommit(ownerCtx(), connect.NewRequest(&pb.GetCommitRequest{Repo: "demo", Revision: "main"}))
111111 requireCode(t, connect.CodeNotFound, err)
112112 })
113113 t.Run("ResolveRevision ok", func(t *testing.T) {
114114 t.Parallel()
115- browse := mocks.NewBrowse(t)
115+ browse := mocks.NewBrowseService(t)
116116 browse.EXPECT().ResolveRevision(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), mock.Anything).
117117 Return(domain.ResolvedRevision{Ref: "refs/heads/main"}, nil).Once()
118118 resp, err := (&gitService{browse}).ResolveRevision(ownerCtx(), connect.NewRequest(&pb.ResolveRevisionRequest{Repo: "demo", Revision: "main"}))
@@ -138,10 +138,10 @@ func TestGitServiceListCommits(t *testing.T) {
138138 for _, tt := range tests {
139139 t.Run(tt.name, func(t *testing.T) {
140140 t.Parallel()
141- browse := mocks.NewBrowse(t)
141+ browse := mocks.NewBrowseService(t)
142142 if tt.wantCall {
143143 browse.EXPECT().ListCommits(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), mock.Anything).
144- Return(app.CommitPage{}, tt.usecase).Once()
144+ Return(ports.HistoryPage{}, tt.usecase).Once()
145145 }
146146 _, err := (&gitService{browse}).ListCommits(ownerCtx(), connect.NewRequest(tt.req))
147147 if tt.wantCode != 0 {
@@ -167,7 +167,7 @@ func TestGitServiceGetBlobRejectsBadInput(t *testing.T) {
167167 for _, tt := range tests {
168168 t.Run(tt.name, func(t *testing.T) {
169169 t.Parallel()
170- err := (&gitService{mocks.NewBrowse(t)}).GetBlob(ownerCtx(), connect.NewRequest(tt.req), nil)
170+ err := (&gitService{mocks.NewBrowseService(t)}).GetBlob(ownerCtx(), connect.NewRequest(tt.req), nil)
171171 requireCode(t, connect.CodeInvalidArgument, err)
172172 })
173173 }
M internal/adapters/rpc/interceptor.go +3 -2
@@ -18,6 +18,7 @@ import (
1818 "bnema.dev/ermite-api/gen/ermite/ermiteconnect"
1919 "bnema.dev/ermite/internal/clientlimit"
2020 "bnema.dev/ermite/internal/domain"
21+ "bnema.dev/ermite/internal/ports"
2122 )
2223
2324 var errInternal = errors.New("internal error")
@@ -37,8 +38,8 @@ var errInternal = errors.New("internal error")
3738 // put there by a server's base context, is ignored and the request is
3839 // authenticated from its headers.
3940 type interceptor struct {
40- auth Authenticator
41- limiter FailureLimiter
41+ auth ports.AuthService
42+ limiter ports.FailureLimiter
4243 trusted []netip.Prefix
4344 }
4445
M internal/adapters/rpc/interceptor_test.go +3 -3
@@ -15,8 +15,8 @@ import (
1515 "github.com/stretchr/testify/require"
1616
1717 "bnema.dev/ermite-api/gen/ermite/ermiteconnect"
18- "bnema.dev/ermite/internal/adapters/rpc/mocks"
1918 "bnema.dev/ermite/internal/domain"
19+ "bnema.dev/ermite/internal/ports/mocks"
2020 )
2121
2222 const peer = "203.0.113.7:4242"
@@ -69,7 +69,7 @@ func TestInterceptorAuthenticate(t *testing.T) {
6969 for _, tt := range tests {
7070 t.Run(tt.name, func(t *testing.T) {
7171 t.Parallel()
72- auth := mocks.NewAuthenticator(t)
72+ auth := mocks.NewAuthService(t)
7373 limiter := mocks.NewFailureLimiter(t)
7474 if tt.wantLimit {
7575 wait := time.Duration(0)
@@ -117,7 +117,7 @@ func TestInterceptorIgnoresForwardedForFromUntrustedPeer(t *testing.T) {
117117 limiter.EXPECT().Blocked(mock.Anything, "203.0.113.7").Return(false, time.Duration(0)).Once()
118118 h := bearerHeader("")
119119 h.Set("X-Forwarded-For", "198.51.100.1")
120- i := newInterceptor(Config{Auth: mocks.NewAuthenticator(t), Limiter: limiter})
120+ i := newInterceptor(Config{Auth: mocks.NewAuthService(t), Limiter: limiter})
121121 _, err := i.authenticate(context.Background(), ermiteconnect.RepoServiceListReposProcedure, peer, h)
122122 require.NoError(t, err)
123123 }
M internal/adapters/rpc/mergerequest.go +6 -6
@@ -10,12 +10,12 @@ import (
1010 "connectrpc.com/connect"
1111
1212 pb "bnema.dev/ermite-api/gen/ermite"
13- "bnema.dev/ermite/internal/app"
1413 "bnema.dev/ermite/internal/domain"
14+ "bnema.dev/ermite/internal/ports"
1515 )
1616
1717 // mergeRequestService implements ermiteconnect.MergeRequestServiceHandler.
18-type mergeRequestService struct{ mrs MergeRequests }
18+type mergeRequestService struct{ mrs ports.MergeRequestService }
1919
2020 func mergeRequestStateToPB(s domain.MRState) pb.MergeRequestState {
2121 switch s {
@@ -129,7 +129,7 @@ func (s *mergeRequestService) CreateMergeRequest(ctx context.Context, req *conne
129129 if err != nil {
130130 return nil, connectError(ctx, err)
131131 }
132- mr, err := s.mrs.Create(ctx, caller(ctx), name, app.CreateMergeRequestParams{
132+ mr, err := s.mrs.Create(ctx, caller(ctx), name, ports.CreateMergeRequestParams{
133133 Title: req.Msg.GetTitle(), Description: req.Msg.GetDescription(),
134134 Source: req.Msg.GetSourceBranch(), Target: req.Msg.GetTargetBranch(),
135135 })
@@ -148,7 +148,7 @@ func (s *mergeRequestService) ListMergeRequests(ctx context.Context, req *connec
148148 if err != nil {
149149 return nil, connectError(ctx, err)
150150 }
151- page, err := s.mrs.List(ctx, caller(ctx), name, app.ListMergeRequestsParams{
151+ page, err := s.mrs.List(ctx, caller(ctx), name, ports.ListMergeRequestsParams{
152152 State: state, PageSize: int(req.Msg.GetPageSize()), PageToken: req.Msg.GetPageToken(),
153153 })
154154 if err != nil {
@@ -197,7 +197,7 @@ func (s *mergeRequestService) AddComment(ctx context.Context, req *connect.Reque
197197 if err != nil {
198198 return nil, connectError(ctx, err)
199199 }
200- c, err := s.mrs.AddComment(ctx, caller(ctx), name, req.Msg.GetNumber(), app.AddCommentParams{
200+ c, err := s.mrs.AddComment(ctx, caller(ctx), name, req.Msg.GetNumber(), ports.AddCommentParams{
201201 Path: req.Msg.GetPath(), Line: int(req.Msg.GetLine()), Side: side, Text: req.Msg.GetText(),
202202 })
203203 if err != nil {
@@ -245,7 +245,7 @@ func (s *mergeRequestService) MergeMergeRequest(ctx context.Context, req *connec
245245 if err != nil {
246246 return nil, connectError(ctx, err)
247247 }
248- res, err := s.mrs.Merge(ctx, caller(ctx), name, req.Msg.GetNumber(), app.MergeParams{
248+ res, err := s.mrs.Merge(ctx, caller(ctx), name, req.Msg.GetNumber(), ports.MergeParams{
249249 Mode: mode, HeadID: head, DeleteSource: req.Msg.GetDeleteSourceBranch(),
250250 })
251251 if err != nil {
M internal/adapters/rpc/mergerequest_test.go +19 -19
@@ -11,9 +11,9 @@ import (
1111 "github.com/stretchr/testify/require"
1212
1313 pb "bnema.dev/ermite-api/gen/ermite"
14- "bnema.dev/ermite/internal/adapters/rpc/mocks"
15- "bnema.dev/ermite/internal/app"
1614 "bnema.dev/ermite/internal/domain"
15+ "bnema.dev/ermite/internal/ports"
16+ "bnema.dev/ermite/internal/ports/mocks"
1717 )
1818
1919 func TestMergeRequestServiceCreate(t *testing.T) {
@@ -35,9 +35,9 @@ func TestMergeRequestServiceCreate(t *testing.T) {
3535 for _, tt := range tests {
3636 t.Run(tt.name, func(t *testing.T) {
3737 t.Parallel()
38- mrs := mocks.NewMergeRequests(t)
38+ mrs := mocks.NewMergeRequestService(t)
3939 if tt.wantCall {
40- mrs.EXPECT().Create(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), app.CreateMergeRequestParams{
40+ mrs.EXPECT().Create(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), ports.CreateMergeRequestParams{
4141 Title: "t", Description: "d", Source: "feat", Target: "main",
4242 }).Return(domain.MergeRequest{Number: 7, Source: "feat", Target: "main", State: domain.MRStateOpen}, tt.usecase).Once()
4343 }
@@ -57,17 +57,17 @@ func TestMergeRequestServiceCreate(t *testing.T) {
5757
5858 func TestMergeRequestServiceListRejectsUnknownState(t *testing.T) {
5959 t.Parallel()
60- _, err := (&mergeRequestService{mocks.NewMergeRequests(t)}).ListMergeRequests(ownerCtx(),
60+ _, err := (&mergeRequestService{mocks.NewMergeRequestService(t)}).ListMergeRequests(ownerCtx(),
6161 connect.NewRequest(&pb.ListMergeRequestsRequest{Repo: "demo", State: pb.MergeRequestState(99)}))
6262 requireCode(t, connect.CodeInvalidArgument, err)
6363 }
6464
6565 func TestMergeRequestServiceListFiltersByState(t *testing.T) {
6666 t.Parallel()
67- mrs := mocks.NewMergeRequests(t)
68- mrs.EXPECT().List(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), app.ListMergeRequestsParams{
67+ mrs := mocks.NewMergeRequestService(t)
68+ mrs.EXPECT().List(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), ports.ListMergeRequestsParams{
6969 State: domain.MRStateMerged, PageSize: 3, PageToken: "tok",
70- }).Return(app.MergeRequestPage{MergeRequests: []domain.MergeRequest{{Number: 1}}, NextPageToken: "n"}, nil).Once()
70+ }).Return(ports.MergeRequestPage{MergeRequests: []domain.MergeRequest{{Number: 1}}, NextPageToken: "n"}, nil).Once()
7171 resp, err := (&mergeRequestService{mrs}).ListMergeRequests(ownerCtx(), connect.NewRequest(&pb.ListMergeRequestsRequest{
7272 Repo: "demo", State: pb.MergeRequestState_MERGE_REQUEST_STATE_MERGED, PageSize: 3, PageToken: "tok",
7373 }))
@@ -94,7 +94,7 @@ func TestMergeRequestServiceAddComment(t *testing.T) {
9494 for _, tt := range tests {
9595 t.Run(tt.name, func(t *testing.T) {
9696 t.Parallel()
97- mrs := mocks.NewMergeRequests(t)
97+ mrs := mocks.NewMergeRequestService(t)
9898 if tt.wantCall {
9999 mrs.EXPECT().AddComment(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(2), mock.Anything).
100100 Return(domain.Comment{ID: 5}, tt.usecase).Once()
@@ -117,7 +117,7 @@ func TestMergeRequestServiceResolveCommentMapsUnissuedIDsToZero(t *testing.T) {
117117 for _, id := range []string{"", "abc", "-1", "0", "007", "99999999999999999999"} {
118118 t.Run(id, func(t *testing.T) {
119119 t.Parallel()
120- mrs := mocks.NewMergeRequests(t)
120+ mrs := mocks.NewMergeRequestService(t)
121121 mrs.EXPECT().ResolveComment(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(1), int64(0), true).
122122 Return(domain.Comment{}, domain.ErrNotFound).Once()
123123 _, err := (&mergeRequestService{mrs}).ResolveComment(ownerCtx(), connect.NewRequest(&pb.ResolveCommentRequest{
@@ -148,11 +148,11 @@ func TestMergeRequestServiceMerge(t *testing.T) {
148148 for _, tt := range tests {
149149 t.Run(tt.name, func(t *testing.T) {
150150 t.Parallel()
151- mrs := mocks.NewMergeRequests(t)
151+ mrs := mocks.NewMergeRequestService(t)
152152 if tt.wantCall {
153- mrs.EXPECT().Merge(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(1), mock.MatchedBy(func(p app.MergeParams) bool {
153+ mrs.EXPECT().Merge(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(1), mock.MatchedBy(func(p ports.MergeParams) bool {
154154 return p.DeleteSource == tt.req.GetDeleteSourceBranch() && (tt.req.GetHeadId() == "") == p.HeadID.IsZero()
155- })).Return(app.MergeResult{MergeRequest: domain.MergeRequest{Number: 1}, SourceDeleted: true}, tt.usecase).Once()
155+ })).Return(ports.MergeResult{MergeRequest: domain.MergeRequest{Number: 1}, SourceDeleted: true}, tt.usecase).Once()
156156 }
157157 resp, err := (&mergeRequestService{mrs}).MergeMergeRequest(ownerCtx(), connect.NewRequest(tt.req))
158158 if tt.wantCode != 0 {
@@ -169,14 +169,14 @@ func TestMergeRequestServiceCloseAndMarkRead(t *testing.T) {
169169 t.Parallel()
170170 t.Run("close not found", func(t *testing.T) {
171171 t.Parallel()
172- mrs := mocks.NewMergeRequests(t)
172+ mrs := mocks.NewMergeRequestService(t)
173173 mrs.EXPECT().Close(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(9)).Return(domain.MergeRequest{}, domain.ErrNotFound).Once()
174174 _, err := (&mergeRequestService{mrs}).CloseMergeRequest(ownerCtx(), connect.NewRequest(&pb.CloseMergeRequestRequest{Repo: "demo", Number: 9}))
175175 requireCode(t, connect.CodeNotFound, err)
176176 })
177177 t.Run("close ok", func(t *testing.T) {
178178 t.Parallel()
179- mrs := mocks.NewMergeRequests(t)
179+ mrs := mocks.NewMergeRequestService(t)
180180 mrs.EXPECT().Close(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(9)).Return(domain.MergeRequest{Number: 9, State: domain.MRStateClosed}, nil).Once()
181181 resp, err := (&mergeRequestService{mrs}).CloseMergeRequest(ownerCtx(), connect.NewRequest(&pb.CloseMergeRequestRequest{Repo: "demo", Number: 9}))
182182 require.NoError(t, err)
@@ -184,13 +184,13 @@ func TestMergeRequestServiceCloseAndMarkRead(t *testing.T) {
184184 })
185185 t.Run("mark read rejects a short head", func(t *testing.T) {
186186 t.Parallel()
187- _, err := (&mergeRequestService{mocks.NewMergeRequests(t)}).MarkRead(ownerCtx(),
187+ _, err := (&mergeRequestService{mocks.NewMergeRequestService(t)}).MarkRead(ownerCtx(),
188188 connect.NewRequest(&pb.MarkReadRequest{Repo: "demo", Number: 1, HeadId: "abc"}))
189189 requireCode(t, connect.CodeInvalidArgument, err)
190190 })
191191 t.Run("mark read unauthenticated", func(t *testing.T) {
192192 t.Parallel()
193- mrs := mocks.NewMergeRequests(t)
193+ mrs := mocks.NewMergeRequestService(t)
194194 mrs.EXPECT().MarkRead(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(1), domain.ObjectID{}).Return(domain.ObjectID{}, domain.ErrUnauthenticated).Once()
195195 _, err := (&mergeRequestService{mrs}).MarkRead(ownerCtx(), connect.NewRequest(&pb.MarkReadRequest{Repo: "demo", Number: 1}))
196196 requireCode(t, connect.CodeUnauthenticated, err)
@@ -211,9 +211,9 @@ func TestMergeRequestServiceGet(t *testing.T) {
211211 for _, tt := range tests {
212212 t.Run(tt.name, func(t *testing.T) {
213213 t.Parallel()
214- mrs := mocks.NewMergeRequests(t)
214+ mrs := mocks.NewMergeRequestService(t)
215215 mrs.EXPECT().Get(mock.Anything, domain.Owner(), mustRepoName(t, "demo"), int64(4), true).
216- Return(app.MergeRequestDetail{MergeRequest: domain.MergeRequest{Number: 4}, CommitsTruncated: true}, tt.usecase).Once()
216+ Return(ports.MergeRequestDetail{MergeRequest: domain.MergeRequest{Number: 4}, CommitsTruncated: true}, tt.usecase).Once()
217217 resp, err := (&mergeRequestService{mrs}).GetMergeRequest(ownerCtx(), connect.NewRequest(&pb.GetMergeRequestRequest{Repo: "demo", Number: 4, SinceLastRead: true}))
218218 if tt.wantCode != 0 {
219219 requireCode(t, tt.wantCode, err)
D internal/adapters/rpc/mocks/Authenticator.go +0 -114
@@ -1,114 +0,0 @@
1-// Code generated by mockery; DO NOT EDIT.
2-// github.com/vektra/mockery
3-// template: testify
4-
5-package mocks
6-
7-import (
8- "context"
9-
10- "bnema.dev/ermite/internal/domain"
11- mock "github.com/stretchr/testify/mock"
12-)
13-
14-// NewAuthenticator creates a new instance of Authenticator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
15-// The first argument is typically a *testing.T value.
16-func NewAuthenticator(t interface {
17- mock.TestingT
18- Cleanup(func())
19-}) *Authenticator {
20- if helper, ok := t.(interface{ Helper() }); ok {
21- helper.Helper()
22- }
23-
24- mock := &Authenticator{}
25- mock.Mock.Test(t)
26-
27- t.Cleanup(func() {
28- if helper, ok := t.(interface{ Helper() }); ok {
29- helper.Helper()
30- }
31- mock.AssertExpectations(t)
32- })
33-
34- return mock
35-}
36-
37-// Authenticator is an autogenerated mock type for the Authenticator type
38-type Authenticator struct {
39- mock.Mock
40-}
41-
42-type Authenticator_Expecter struct {
43- mock *mock.Mock
44-}
45-
46-func (_m *Authenticator) EXPECT() *Authenticator_Expecter {
47- return &Authenticator_Expecter{mock: &_m.Mock}
48-}
49-
50-// Authenticate provides a mock function for the type Authenticator
51-func (_mock *Authenticator) Authenticate(ctx context.Context, secret string) (domain.Principal, error) {
52- ret := _mock.Called(ctx, secret)
53-
54- if len(ret) == 0 {
55- panic("no return value specified for Authenticate")
56- }
57-
58- var r0 domain.Principal
59- var r1 error
60- if returnFunc, ok := ret.Get(0).(func(context.Context, string) (domain.Principal, error)); ok {
61- return returnFunc(ctx, secret)
62- }
63- if returnFunc, ok := ret.Get(0).(func(context.Context, string) domain.Principal); ok {
64- r0 = returnFunc(ctx, secret)
65- } else {
66- r0 = ret.Get(0).(domain.Principal)
67- }
68- if returnFunc, ok := ret.Get(1).(func(context.Context, string) error); ok {
69- r1 = returnFunc(ctx, secret)
70- } else {
71- r1 = ret.Error(1)
72- }
73- return r0, r1
74-}
75-
76-// Authenticator_Authenticate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Authenticate'
77-type Authenticator_Authenticate_Call struct {
78- *mock.Call
79-}
80-
81-// Authenticate is a helper method to define mock.On call
82-// - ctx context.Context
83-// - secret string
84-func (_e *Authenticator_Expecter) Authenticate(ctx any, secret any) *Authenticator_Authenticate_Call {
85- return &Authenticator_Authenticate_Call{Call: _e.mock.On("Authenticate", ctx, secret)}
86-}
87-
88-func (_c *Authenticator_Authenticate_Call) Run(run func(ctx context.Context, secret string)) *Authenticator_Authenticate_Call {
89- _c.Call.Run(func(args mock.Arguments) {
90- var arg0 context.Context
91- if args[0] != nil {
92- arg0 = args[0].(context.Context)
93- }
94- var arg1 string
95- if args[1] != nil {
96- arg1 = args[1].(string)
97- }
98- run(
99- arg0,
100- arg1,
101- )
102- })
103- return _c
104-}
105-
106-func (_c *Authenticator_Authenticate_Call) Return(principal domain.Principal, err error) *Authenticator_Authenticate_Call {
107- _c.Call.Return(principal, err)
108- return _c
109-}
110-
111-func (_c *Authenticator_Authenticate_Call) RunAndReturn(run func(ctx context.Context, secret string) (domain.Principal, error)) *Authenticator_Authenticate_Call {
112- _c.Call.Return(run)
113- return _c
114-}
D internal/adapters/rpc/mocks/Events.go +0 -118
@@ -1,118 +0,0 @@
1-// Code generated by mockery; DO NOT EDIT.
2-// github.com/vektra/mockery
3-// template: testify
4-
5-package mocks
6-
7-import (
8- "context"
9-
10- "bnema.dev/ermite/internal/app"
11- "bnema.dev/ermite/internal/domain"
12- mock "github.com/stretchr/testify/mock"
13-)
14-
15-// NewEvents creates a new instance of Events. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
16-// The first argument is typically a *testing.T value.
17-func NewEvents(t interface {
18- mock.TestingT
19- Cleanup(func())
20-}) *Events {
21- if helper, ok := t.(interface{ Helper() }); ok {
22- helper.Helper()
23- }
24-
25- mock := &Events{}
26- mock.Mock.Test(t)
27-
28- t.Cleanup(func() {
29- if helper, ok := t.(interface{ Helper() }); ok {
30- helper.Helper()
31- }
32- mock.AssertExpectations(t)
33- })
34-
35- return mock
36-}
37-
38-// Events is an autogenerated mock type for the Events type
39-type Events struct {
40- mock.Mock
41-}
42-
43-type Events_Expecter struct {
44- mock *mock.Mock
45-}
46-
47-func (_m *Events) EXPECT() *Events_Expecter {
48- return &Events_Expecter{mock: &_m.Mock}
49-}
50-
51-// Watch provides a mock function for the type Events
52-func (_mock *Events) Watch(ctx context.Context, p domain.Principal, params app.WatchParams, send func(app.WatchEvent) error) error {
53- ret := _mock.Called(ctx, p, params, send)
54-
55- if len(ret) == 0 {
56- panic("no return value specified for Watch")
57- }
58-
59- var r0 error
60- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, app.WatchParams, func(app.WatchEvent) error) error); ok {
61- r0 = returnFunc(ctx, p, params, send)
62- } else {
63- r0 = ret.Error(0)
64- }
65- return r0
66-}
67-
68-// Events_Watch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Watch'
69-type Events_Watch_Call struct {
70- *mock.Call
71-}
72-
73-// Watch is a helper method to define mock.On call
74-// - ctx context.Context
75-// - p domain.Principal
76-// - params app.WatchParams
77-// - send func(app.WatchEvent) error
78-func (_e *Events_Expecter) Watch(ctx any, p any, params any, send any) *Events_Watch_Call {
79- return &Events_Watch_Call{Call: _e.mock.On("Watch", ctx, p, params, send)}
80-}
81-
82-func (_c *Events_Watch_Call) Run(run func(ctx context.Context, p domain.Principal, params app.WatchParams, send func(app.WatchEvent) error)) *Events_Watch_Call {
83- _c.Call.Run(func(args mock.Arguments) {
84- var arg0 context.Context
85- if args[0] != nil {
86- arg0 = args[0].(context.Context)
87- }
88- var arg1 domain.Principal
89- if args[1] != nil {
90- arg1 = args[1].(domain.Principal)
91- }
92- var arg2 app.WatchParams
93- if args[2] != nil {
94- arg2 = args[2].(app.WatchParams)
95- }
96- var arg3 func(app.WatchEvent) error
97- if args[3] != nil {
98- arg3 = args[3].(func(app.WatchEvent) error)
99- }
100- run(
101- arg0,
102- arg1,
103- arg2,
104- arg3,
105- )
106- })
107- return _c
108-}
109-
110-func (_c *Events_Watch_Call) Return(err error) *Events_Watch_Call {
111- _c.Call.Return(err)
112- return _c
113-}
114-
115-func (_c *Events_Watch_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, params app.WatchParams, send func(app.WatchEvent) error) error) *Events_Watch_Call {
116- _c.Call.Return(run)
117- return _c
118-}
D internal/adapters/rpc/mocks/FailureLimiter.go +0 -160
@@ -1,160 +0,0 @@
1-// Code generated by mockery; DO NOT EDIT.
2-// github.com/vektra/mockery
3-// template: testify
4-
5-package mocks
6-
7-import (
8- "context"
9- "time"
10-
11- mock "github.com/stretchr/testify/mock"
12-)
13-
14-// NewFailureLimiter creates a new instance of FailureLimiter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
15-// The first argument is typically a *testing.T value.
16-func NewFailureLimiter(t interface {
17- mock.TestingT
18- Cleanup(func())
19-}) *FailureLimiter {
20- if helper, ok := t.(interface{ Helper() }); ok {
21- helper.Helper()
22- }
23-
24- mock := &FailureLimiter{}
25- mock.Mock.Test(t)
26-
27- t.Cleanup(func() {
28- if helper, ok := t.(interface{ Helper() }); ok {
29- helper.Helper()
30- }
31- mock.AssertExpectations(t)
32- })
33-
34- return mock
35-}
36-
37-// FailureLimiter is an autogenerated mock type for the FailureLimiter type
38-type FailureLimiter struct {
39- mock.Mock
40-}
41-
42-type FailureLimiter_Expecter struct {
43- mock *mock.Mock
44-}
45-
46-func (_m *FailureLimiter) EXPECT() *FailureLimiter_Expecter {
47- return &FailureLimiter_Expecter{mock: &_m.Mock}
48-}
49-
50-// Blocked provides a mock function for the type FailureLimiter
51-func (_mock *FailureLimiter) Blocked(ctx context.Context, addr string) (bool, time.Duration) {
52- ret := _mock.Called(ctx, addr)
53-
54- if len(ret) == 0 {
55- panic("no return value specified for Blocked")
56- }
57-
58- var r0 bool
59- var r1 time.Duration
60- if returnFunc, ok := ret.Get(0).(func(context.Context, string) (bool, time.Duration)); ok {
61- return returnFunc(ctx, addr)
62- }
63- if returnFunc, ok := ret.Get(0).(func(context.Context, string) bool); ok {
64- r0 = returnFunc(ctx, addr)
65- } else {
66- r0 = ret.Get(0).(bool)
67- }
68- if returnFunc, ok := ret.Get(1).(func(context.Context, string) time.Duration); ok {
69- r1 = returnFunc(ctx, addr)
70- } else {
71- r1 = ret.Get(1).(time.Duration)
72- }
73- return r0, r1
74-}
75-
76-// FailureLimiter_Blocked_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Blocked'
77-type FailureLimiter_Blocked_Call struct {
78- *mock.Call
79-}
80-
81-// Blocked is a helper method to define mock.On call
82-// - ctx context.Context
83-// - addr string
84-func (_e *FailureLimiter_Expecter) Blocked(ctx any, addr any) *FailureLimiter_Blocked_Call {
85- return &FailureLimiter_Blocked_Call{Call: _e.mock.On("Blocked", ctx, addr)}
86-}
87-
88-func (_c *FailureLimiter_Blocked_Call) Run(run func(ctx context.Context, addr string)) *FailureLimiter_Blocked_Call {
89- _c.Call.Run(func(args mock.Arguments) {
90- var arg0 context.Context
91- if args[0] != nil {
92- arg0 = args[0].(context.Context)
93- }
94- var arg1 string
95- if args[1] != nil {
96- arg1 = args[1].(string)
97- }
98- run(
99- arg0,
100- arg1,
101- )
102- })
103- return _c
104-}
105-
106-func (_c *FailureLimiter_Blocked_Call) Return(b bool, duration time.Duration) *FailureLimiter_Blocked_Call {
107- _c.Call.Return(b, duration)
108- return _c
109-}
110-
111-func (_c *FailureLimiter_Blocked_Call) RunAndReturn(run func(ctx context.Context, addr string) (bool, time.Duration)) *FailureLimiter_Blocked_Call {
112- _c.Call.Return(run)
113- return _c
114-}
115-
116-// Fail provides a mock function for the type FailureLimiter
117-func (_mock *FailureLimiter) Fail(ctx context.Context, addr string) {
118- _mock.Called(ctx, addr)
119- return
120-}
121-
122-// FailureLimiter_Fail_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Fail'
123-type FailureLimiter_Fail_Call struct {
124- *mock.Call
125-}
126-
127-// Fail is a helper method to define mock.On call
128-// - ctx context.Context
129-// - addr string
130-func (_e *FailureLimiter_Expecter) Fail(ctx any, addr any) *FailureLimiter_Fail_Call {
131- return &FailureLimiter_Fail_Call{Call: _e.mock.On("Fail", ctx, addr)}
132-}
133-
134-func (_c *FailureLimiter_Fail_Call) Run(run func(ctx context.Context, addr string)) *FailureLimiter_Fail_Call {
135- _c.Call.Run(func(args mock.Arguments) {
136- var arg0 context.Context
137- if args[0] != nil {
138- arg0 = args[0].(context.Context)
139- }
140- var arg1 string
141- if args[1] != nil {
142- arg1 = args[1].(string)
143- }
144- run(
145- arg0,
146- arg1,
147- )
148- })
149- return _c
150-}
151-
152-func (_c *FailureLimiter_Fail_Call) Return() *FailureLimiter_Fail_Call {
153- _c.Call.Return()
154- return _c
155-}
156-
157-func (_c *FailureLimiter_Fail_Call) RunAndReturn(run func(ctx context.Context, addr string)) *FailureLimiter_Fail_Call {
158- _c.Run(run)
159- return _c
160-}
D internal/adapters/rpc/mocks/Sessions.go +0 -172
@@ -1,172 +0,0 @@
1-// Code generated by mockery; DO NOT EDIT.
2-// github.com/vektra/mockery
3-// template: testify
4-
5-package mocks
6-
7-import (
8- "context"
9-
10- "bnema.dev/ermite/internal/app"
11- "bnema.dev/ermite/internal/domain"
12- mock "github.com/stretchr/testify/mock"
13-)
14-
15-// NewSessions creates a new instance of Sessions. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
16-// The first argument is typically a *testing.T value.
17-func NewSessions(t interface {
18- mock.TestingT
19- Cleanup(func())
20-}) *Sessions {
21- if helper, ok := t.(interface{ Helper() }); ok {
22- helper.Helper()
23- }
24-
25- mock := &Sessions{}
26- mock.Mock.Test(t)
27-
28- t.Cleanup(func() {
29- if helper, ok := t.(interface{ Helper() }); ok {
30- helper.Helper()
31- }
32- mock.AssertExpectations(t)
33- })
34-
35- return mock
36-}
37-
38-// Sessions is an autogenerated mock type for the Sessions type
39-type Sessions struct {
40- mock.Mock
41-}
42-
43-type Sessions_Expecter struct {
44- mock *mock.Mock
45-}
46-
47-func (_m *Sessions) EXPECT() *Sessions_Expecter {
48- return &Sessions_Expecter{mock: &_m.Mock}
49-}
50-
51-// CreateLoginLink provides a mock function for the type Sessions
52-func (_mock *Sessions) CreateLoginLink(ctx context.Context, p domain.Principal) (app.LoginLink, error) {
53- ret := _mock.Called(ctx, p)
54-
55- if len(ret) == 0 {
56- panic("no return value specified for CreateLoginLink")
57- }
58-
59- var r0 app.LoginLink
60- var r1 error
61- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) (app.LoginLink, error)); ok {
62- return returnFunc(ctx, p)
63- }
64- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) app.LoginLink); ok {
65- r0 = returnFunc(ctx, p)
66- } else {
67- r0 = ret.Get(0).(app.LoginLink)
68- }
69- if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal) error); ok {
70- r1 = returnFunc(ctx, p)
71- } else {
72- r1 = ret.Error(1)
73- }
74- return r0, r1
75-}
76-
77-// Sessions_CreateLoginLink_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateLoginLink'
78-type Sessions_CreateLoginLink_Call struct {
79- *mock.Call
80-}
81-
82-// CreateLoginLink is a helper method to define mock.On call
83-// - ctx context.Context
84-// - p domain.Principal
85-func (_e *Sessions_Expecter) CreateLoginLink(ctx any, p any) *Sessions_CreateLoginLink_Call {
86- return &Sessions_CreateLoginLink_Call{Call: _e.mock.On("CreateLoginLink", ctx, p)}
87-}
88-
89-func (_c *Sessions_CreateLoginLink_Call) Run(run func(ctx context.Context, p domain.Principal)) *Sessions_CreateLoginLink_Call {
90- _c.Call.Run(func(args mock.Arguments) {
91- var arg0 context.Context
92- if args[0] != nil {
93- arg0 = args[0].(context.Context)
94- }
95- var arg1 domain.Principal
96- if args[1] != nil {
97- arg1 = args[1].(domain.Principal)
98- }
99- run(
100- arg0,
101- arg1,
102- )
103- })
104- return _c
105-}
106-
107-func (_c *Sessions_CreateLoginLink_Call) Return(loginLink app.LoginLink, err error) *Sessions_CreateLoginLink_Call {
108- _c.Call.Return(loginLink, err)
109- return _c
110-}
111-
112-func (_c *Sessions_CreateLoginLink_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal) (app.LoginLink, error)) *Sessions_CreateLoginLink_Call {
113- _c.Call.Return(run)
114- return _c
115-}
116-
117-// SignOutEverywhere provides a mock function for the type Sessions
118-func (_mock *Sessions) SignOutEverywhere(ctx context.Context, p domain.Principal) error {
119- ret := _mock.Called(ctx, p)
120-
121- if len(ret) == 0 {
122- panic("no return value specified for SignOutEverywhere")
123- }
124-
125- var r0 error
126- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) error); ok {
127- r0 = returnFunc(ctx, p)
128- } else {
129- r0 = ret.Error(0)
130- }
131- return r0
132-}
133-
134-// Sessions_SignOutEverywhere_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SignOutEverywhere'
135-type Sessions_SignOutEverywhere_Call struct {
136- *mock.Call
137-}
138-
139-// SignOutEverywhere is a helper method to define mock.On call
140-// - ctx context.Context
141-// - p domain.Principal
142-func (_e *Sessions_Expecter) SignOutEverywhere(ctx any, p any) *Sessions_SignOutEverywhere_Call {
143- return &Sessions_SignOutEverywhere_Call{Call: _e.mock.On("SignOutEverywhere", ctx, p)}
144-}
145-
146-func (_c *Sessions_SignOutEverywhere_Call) Run(run func(ctx context.Context, p domain.Principal)) *Sessions_SignOutEverywhere_Call {
147- _c.Call.Run(func(args mock.Arguments) {
148- var arg0 context.Context
149- if args[0] != nil {
150- arg0 = args[0].(context.Context)
151- }
152- var arg1 domain.Principal
153- if args[1] != nil {
154- arg1 = args[1].(domain.Principal)
155- }
156- run(
157- arg0,
158- arg1,
159- )
160- })
161- return _c
162-}
163-
164-func (_c *Sessions_SignOutEverywhere_Call) Return(err error) *Sessions_SignOutEverywhere_Call {
165- _c.Call.Return(err)
166- return _c
167-}
168-
169-func (_c *Sessions_SignOutEverywhere_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal) error) *Sessions_SignOutEverywhere_Call {
170- _c.Call.Return(run)
171- return _c
172-}
D internal/adapters/rpc/mocks/SigningKey.go +0 -90
@@ -1,90 +0,0 @@
1-// Code generated by mockery; DO NOT EDIT.
2-// github.com/vektra/mockery
3-// template: testify
4-
5-package mocks
6-
7-import (
8- "bnema.dev/ermite/internal/domain"
9- mock "github.com/stretchr/testify/mock"
10-)
11-
12-// NewSigningKey creates a new instance of SigningKey. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
13-// The first argument is typically a *testing.T value.
14-func NewSigningKey(t interface {
15- mock.TestingT
16- Cleanup(func())
17-}) *SigningKey {
18- if helper, ok := t.(interface{ Helper() }); ok {
19- helper.Helper()
20- }
21-
22- mock := &SigningKey{}
23- mock.Mock.Test(t)
24-
25- t.Cleanup(func() {
26- if helper, ok := t.(interface{ Helper() }); ok {
27- helper.Helper()
28- }
29- mock.AssertExpectations(t)
30- })
31-
32- return mock
33-}
34-
35-// SigningKey is an autogenerated mock type for the SigningKey type
36-type SigningKey struct {
37- mock.Mock
38-}
39-
40-type SigningKey_Expecter struct {
41- mock *mock.Mock
42-}
43-
44-func (_m *SigningKey) EXPECT() *SigningKey_Expecter {
45- return &SigningKey_Expecter{mock: &_m.Mock}
46-}
47-
48-// Info provides a mock function for the type SigningKey
49-func (_mock *SigningKey) Info() domain.SigningKeyInfo {
50- ret := _mock.Called()
51-
52- if len(ret) == 0 {
53- panic("no return value specified for Info")
54- }
55-
56- var r0 domain.SigningKeyInfo
57- if returnFunc, ok := ret.Get(0).(func() domain.SigningKeyInfo); ok {
58- r0 = returnFunc()
59- } else {
60- r0 = ret.Get(0).(domain.SigningKeyInfo)
61- }
62- return r0
63-}
64-
65-// SigningKey_Info_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Info'
66-type SigningKey_Info_Call struct {
67- *mock.Call
68-}
69-
70-// Info is a helper method to define mock.On call
71-func (_e *SigningKey_Expecter) Info() *SigningKey_Info_Call {
72- return &SigningKey_Info_Call{Call: _e.mock.On("Info")}
73-}
74-
75-func (_c *SigningKey_Info_Call) Run(run func()) *SigningKey_Info_Call {
76- _c.Call.Run(func(args mock.Arguments) {
77- run()
78- })
79- return _c
80-}
81-
82-func (_c *SigningKey_Info_Call) Return(signingKeyInfo domain.SigningKeyInfo) *SigningKey_Info_Call {
83- _c.Call.Return(signingKeyInfo)
84- return _c
85-}
86-
87-func (_c *SigningKey_Info_Call) RunAndReturn(run func() domain.SigningKeyInfo) *SigningKey_Info_Call {
88- _c.Call.Return(run)
89- return _c
90-}
D internal/adapters/rpc/mocks/Tokens.go +0 -262
@@ -1,262 +0,0 @@
1-// Code generated by mockery; DO NOT EDIT.
2-// github.com/vektra/mockery
3-// template: testify
4-
5-package mocks
6-
7-import (
8- "context"
9-
10- "bnema.dev/ermite/internal/app"
11- "bnema.dev/ermite/internal/domain"
12- mock "github.com/stretchr/testify/mock"
13-)
14-
15-// NewTokens creates a new instance of Tokens. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
16-// The first argument is typically a *testing.T value.
17-func NewTokens(t interface {
18- mock.TestingT
19- Cleanup(func())
20-}) *Tokens {
21- if helper, ok := t.(interface{ Helper() }); ok {
22- helper.Helper()
23- }
24-
25- mock := &Tokens{}
26- mock.Mock.Test(t)
27-
28- t.Cleanup(func() {
29- if helper, ok := t.(interface{ Helper() }); ok {
30- helper.Helper()
31- }
32- mock.AssertExpectations(t)
33- })
34-
35- return mock
36-}
37-
38-// Tokens is an autogenerated mock type for the Tokens type
39-type Tokens struct {
40- mock.Mock
41-}
42-
43-type Tokens_Expecter struct {
44- mock *mock.Mock
45-}
46-
47-func (_m *Tokens) EXPECT() *Tokens_Expecter {
48- return &Tokens_Expecter{mock: &_m.Mock}
49-}
50-
51-// Create provides a mock function for the type Tokens
52-func (_mock *Tokens) Create(ctx context.Context, p domain.Principal, params app.CreateTokenParams) (app.CreatedToken, error) {
53- ret := _mock.Called(ctx, p, params)
54-
55- if len(ret) == 0 {
56- panic("no return value specified for Create")
57- }
58-
59- var r0 app.CreatedToken
60- var r1 error
61- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, app.CreateTokenParams) (app.CreatedToken, error)); ok {
62- return returnFunc(ctx, p, params)
63- }
64- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, app.CreateTokenParams) app.CreatedToken); ok {
65- r0 = returnFunc(ctx, p, params)
66- } else {
67- r0 = ret.Get(0).(app.CreatedToken)
68- }
69- if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, app.CreateTokenParams) error); ok {
70- r1 = returnFunc(ctx, p, params)
71- } else {
72- r1 = ret.Error(1)
73- }
74- return r0, r1
75-}
76-
77-// Tokens_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create'
78-type Tokens_Create_Call struct {
79- *mock.Call
80-}
81-
82-// Create is a helper method to define mock.On call
83-// - ctx context.Context
84-// - p domain.Principal
85-// - params app.CreateTokenParams
86-func (_e *Tokens_Expecter) Create(ctx any, p any, params any) *Tokens_Create_Call {
87- return &Tokens_Create_Call{Call: _e.mock.On("Create", ctx, p, params)}
88-}
89-
90-func (_c *Tokens_Create_Call) Run(run func(ctx context.Context, p domain.Principal, params app.CreateTokenParams)) *Tokens_Create_Call {
91- _c.Call.Run(func(args mock.Arguments) {
92- var arg0 context.Context
93- if args[0] != nil {
94- arg0 = args[0].(context.Context)
95- }
96- var arg1 domain.Principal
97- if args[1] != nil {
98- arg1 = args[1].(domain.Principal)
99- }
100- var arg2 app.CreateTokenParams
101- if args[2] != nil {
102- arg2 = args[2].(app.CreateTokenParams)
103- }
104- run(
105- arg0,
106- arg1,
107- arg2,
108- )
109- })
110- return _c
111-}
112-
113-func (_c *Tokens_Create_Call) Return(createdToken app.CreatedToken, err error) *Tokens_Create_Call {
114- _c.Call.Return(createdToken, err)
115- return _c
116-}
117-
118-func (_c *Tokens_Create_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, params app.CreateTokenParams) (app.CreatedToken, error)) *Tokens_Create_Call {
119- _c.Call.Return(run)
120- return _c
121-}
122-
123-// ListPage provides a mock function for the type Tokens
124-func (_mock *Tokens) ListPage(ctx context.Context, p domain.Principal, size int, token string) (app.TokenPage, error) {
125- ret := _mock.Called(ctx, p, size, token)
126-
127- if len(ret) == 0 {
128- panic("no return value specified for ListPage")
129- }
130-
131- var r0 app.TokenPage
132- var r1 error
133- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, int, string) (app.TokenPage, error)); ok {
134- return returnFunc(ctx, p, size, token)
135- }
136- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, int, string) app.TokenPage); ok {
137- r0 = returnFunc(ctx, p, size, token)
138- } else {
139- r0 = ret.Get(0).(app.TokenPage)
140- }
141- if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, int, string) error); ok {
142- r1 = returnFunc(ctx, p, size, token)
143- } else {
144- r1 = ret.Error(1)
145- }
146- return r0, r1
147-}
148-
149-// Tokens_ListPage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListPage'
150-type Tokens_ListPage_Call struct {
151- *mock.Call
152-}
153-
154-// ListPage is a helper method to define mock.On call
155-// - ctx context.Context
156-// - p domain.Principal
157-// - size int
158-// - token string
159-func (_e *Tokens_Expecter) ListPage(ctx any, p any, size any, token any) *Tokens_ListPage_Call {
160- return &Tokens_ListPage_Call{Call: _e.mock.On("ListPage", ctx, p, size, token)}
161-}
162-
163-func (_c *Tokens_ListPage_Call) Run(run func(ctx context.Context, p domain.Principal, size int, token string)) *Tokens_ListPage_Call {
164- _c.Call.Run(func(args mock.Arguments) {
165- var arg0 context.Context
166- if args[0] != nil {
167- arg0 = args[0].(context.Context)
168- }
169- var arg1 domain.Principal
170- if args[1] != nil {
171- arg1 = args[1].(domain.Principal)
172- }
173- var arg2 int
174- if args[2] != nil {
175- arg2 = args[2].(int)
176- }
177- var arg3 string
178- if args[3] != nil {
179- arg3 = args[3].(string)
180- }
181- run(
182- arg0,
183- arg1,
184- arg2,
185- arg3,
186- )
187- })
188- return _c
189-}
190-
191-func (_c *Tokens_ListPage_Call) Return(tokenPage app.TokenPage, err error) *Tokens_ListPage_Call {
192- _c.Call.Return(tokenPage, err)
193- return _c
194-}
195-
196-func (_c *Tokens_ListPage_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, size int, token string) (app.TokenPage, error)) *Tokens_ListPage_Call {
197- _c.Call.Return(run)
198- return _c
199-}
200-
201-// Revoke provides a mock function for the type Tokens
202-func (_mock *Tokens) Revoke(ctx context.Context, p domain.Principal, id string) error {
203- ret := _mock.Called(ctx, p, id)
204-
205- if len(ret) == 0 {
206- panic("no return value specified for Revoke")
207- }
208-
209- var r0 error
210- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, string) error); ok {
211- r0 = returnFunc(ctx, p, id)
212- } else {
213- r0 = ret.Error(0)
214- }
215- return r0
216-}
217-
218-// Tokens_Revoke_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Revoke'
219-type Tokens_Revoke_Call struct {
220- *mock.Call
221-}
222-
223-// Revoke is a helper method to define mock.On call
224-// - ctx context.Context
225-// - p domain.Principal
226-// - id string
227-func (_e *Tokens_Expecter) Revoke(ctx any, p any, id any) *Tokens_Revoke_Call {
228- return &Tokens_Revoke_Call{Call: _e.mock.On("Revoke", ctx, p, id)}
229-}
230-
231-func (_c *Tokens_Revoke_Call) Run(run func(ctx context.Context, p domain.Principal, id string)) *Tokens_Revoke_Call {
232- _c.Call.Run(func(args mock.Arguments) {
233- var arg0 context.Context
234- if args[0] != nil {
235- arg0 = args[0].(context.Context)
236- }
237- var arg1 domain.Principal
238- if args[1] != nil {
239- arg1 = args[1].(domain.Principal)
240- }
241- var arg2 string
242- if args[2] != nil {
243- arg2 = args[2].(string)
244- }
245- run(
246- arg0,
247- arg1,
248- arg2,
249- )
250- })
251- return _c
252-}
253-
254-func (_c *Tokens_Revoke_Call) Return(err error) *Tokens_Revoke_Call {
255- _c.Call.Return(err)
256- return _c
257-}
258-
259-func (_c *Tokens_Revoke_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, id string) error) *Tokens_Revoke_Call {
260- _c.Call.Return(run)
261- return _c
262-}
M internal/adapters/rpc/repo.go +6 -6
@@ -9,12 +9,12 @@ import (
99 "connectrpc.com/connect"
1010
1111 pb "bnema.dev/ermite-api/gen/ermite"
12- "bnema.dev/ermite/internal/app"
1312 "bnema.dev/ermite/internal/domain"
13+ "bnema.dev/ermite/internal/ports"
1414 )
1515
1616 // repoService implements ermiteconnect.RepoServiceHandler.
17-type repoService struct{ repos Repos }
17+type repoService struct{ repos ports.RepoService }
1818
1919 func (s *repoService) ListRepos(ctx context.Context, req *connect.Request[pb.ListReposRequest]) (*connect.Response[pb.ListReposResponse], error) {
2020 page, err := s.repos.ListPage(ctx, caller(ctx), int(req.Msg.GetPageSize()), req.Msg.GetPageToken())
@@ -53,7 +53,7 @@ func (s *repoService) CreateRepo(ctx context.Context, req *connect.Request[pb.Cr
5353 if err != nil {
5454 return nil, connectError(ctx, err)
5555 }
56- repo, err := s.repos.Create(ctx, caller(ctx), app.CreateParams{
56+ repo, err := s.repos.Create(ctx, caller(ctx), ports.CreateRepoParams{
5757 Name: name, Description: req.Msg.GetDescription(), Visibility: vis,
5858 })
5959 if err != nil {
@@ -84,8 +84,8 @@ func (s *repoService) UpdateRepo(ctx context.Context, req *connect.Request[pb.Up
8484 // nothing else.
8585 //
8686 //hexcheck:ignore suspicious-business-logic-in-adapter maps the field mask of the contract to the optional fields of the use case; no rule is decided here
87-func updateParams(msg *pb.UpdateRepoRequest) (app.UpdateParams, error) {
88- var params app.UpdateParams
87+func updateParams(msg *pb.UpdateRepoRequest) (ports.UpdateRepoParams, error) {
88+ var params ports.UpdateRepoParams
8989 paths := msg.GetUpdateMask().GetPaths()
9090 if len(paths) == 0 {
9191 return params, fmt.Errorf("%w: update_mask is empty", domain.ErrInvalid)
@@ -135,7 +135,7 @@ func (s *repoService) DeleteRepo(ctx context.Context, req *connect.Request[pb.De
135135 }
136136
137137 // respondRepo adds the default branch to repo and wraps it in a response.
138-func respondRepo[T any](ctx context.Context, repos Repos, repo domain.Repo, wrap func(*pb.Repo) *T) (*connect.Response[T], error) {
138+func respondRepo[T any](ctx context.Context, repos ports.RepoService, repo domain.Repo, wrap func(*pb.Repo) *T) (*connect.Response[T], error) {
139139 view, err := repos.View(ctx, repo)
140140 if err != nil {
141141 return nil, connectError(ctx, err)
M internal/adapters/rpc/repo_test.go +16 -16
@@ -14,9 +14,9 @@ import (
1414 "google.golang.org/protobuf/types/known/fieldmaskpb"
1515
1616 pb "bnema.dev/ermite-api/gen/ermite"
17- "bnema.dev/ermite/internal/adapters/rpc/mocks"
18- "bnema.dev/ermite/internal/app"
1917 "bnema.dev/ermite/internal/domain"
18+ "bnema.dev/ermite/internal/ports"
19+ "bnema.dev/ermite/internal/ports/mocks"
2020 )
2121
2222 // ownerCtx is the context of a call made by the owner, as the interceptor
@@ -60,12 +60,12 @@ func TestRepoServiceGetRepo(t *testing.T) {
6060 for _, tt := range tests {
6161 t.Run(tt.name, func(t *testing.T) {
6262 t.Parallel()
63- repos := mocks.NewRepos(t)
63+ repos := mocks.NewRepoService(t)
6464 if tt.wantCall {
6565 repo := domain.Repo{Name: mustRepoName(t, "demo"), Description: "d", Visibility: domain.VisibilityPublic}
6666 repos.EXPECT().Get(mock.Anything, domain.Owner(), repo.Name).Return(repo, tt.usecase).Once()
6767 if tt.usecase == nil {
68- repos.EXPECT().View(mock.Anything, repo).Return(app.RepoView{Repo: repo, DefaultBranch: "main"}, tt.viewErr).Once()
68+ repos.EXPECT().View(mock.Anything, repo).Return(ports.RepoView{Repo: repo, DefaultBranch: "main"}, tt.viewErr).Once()
6969 }
7070 }
7171 svc := &repoService{repos}
@@ -102,14 +102,14 @@ func TestRepoServiceCreateRepo(t *testing.T) {
102102 for _, tt := range tests {
103103 t.Run(tt.name, func(t *testing.T) {
104104 t.Parallel()
105- repos := mocks.NewRepos(t)
105+ repos := mocks.NewRepoService(t)
106106 if tt.wantCall {
107107 repo := domain.Repo{Name: mustRepoName(t, "demo"), Visibility: domain.VisibilityPrivate}
108- repos.EXPECT().Create(mock.Anything, domain.Owner(), mock.MatchedBy(func(p app.CreateParams) bool {
108+ repos.EXPECT().Create(mock.Anything, domain.Owner(), mock.MatchedBy(func(p ports.CreateRepoParams) bool {
109109 return p.Name.String() == "demo"
110110 })).Return(repo, tt.usecase).Once()
111111 if tt.usecase == nil {
112- repos.EXPECT().View(mock.Anything, repo).Return(app.RepoView{Repo: repo}, nil).Once()
112+ repos.EXPECT().View(mock.Anything, repo).Return(ports.RepoView{Repo: repo}, nil).Once()
113113 }
114114 }
115115 resp, err := (&repoService{repos}).CreateRepo(ownerCtx(), connect.NewRequest(tt.req))
@@ -170,12 +170,12 @@ func TestRepoServiceUpdateRepoValidatesMask(t *testing.T) {
170170 for _, tt := range tests {
171171 t.Run(tt.name, func(t *testing.T) {
172172 t.Parallel()
173- repos := mocks.NewRepos(t)
173+ repos := mocks.NewRepoService(t)
174174 if tt.wantCall {
175175 repo := domain.Repo{Name: mustRepoName(t, "demo"), Description: "new"}
176176 repos.EXPECT().Update(mock.Anything, domain.Owner(), repo.Name, mock.Anything).Return(repo, tt.usecase).Once()
177177 if tt.usecase == nil {
178- repos.EXPECT().View(mock.Anything, repo).Return(app.RepoView{Repo: repo}, nil).Once()
178+ repos.EXPECT().View(mock.Anything, repo).Return(ports.RepoView{Repo: repo}, nil).Once()
179179 }
180180 }
181181 resp, err := (&repoService{repos}).UpdateRepo(ownerCtx(), connect.NewRequest(tt.req))
@@ -206,7 +206,7 @@ func TestRepoServiceDeleteRepo(t *testing.T) {
206206 for _, tt := range tests {
207207 t.Run(tt.name, func(t *testing.T) {
208208 t.Parallel()
209- repos := mocks.NewRepos(t)
209+ repos := mocks.NewRepoService(t)
210210 if tt.wantCall {
211211 repos.EXPECT().Delete(mock.Anything, domain.Owner(), mustRepoName(t, tt.repo)).Return(tt.usecase).Once()
212212 }
@@ -224,12 +224,12 @@ func TestRepoServiceListRepos(t *testing.T) {
224224 t.Parallel()
225225 t.Run("pages and views every repo", func(t *testing.T) {
226226 t.Parallel()
227- repos := mocks.NewRepos(t)
227+ repos := mocks.NewRepoService(t)
228228 a := domain.Repo{Name: mustRepoName(t, "a")}
229229 b := domain.Repo{Name: mustRepoName(t, "b")}
230- repos.EXPECT().ListPage(mock.Anything, domain.Owner(), 2, "tok").Return(app.RepoPage{Repos: []domain.Repo{a, b}, NextPageToken: "next"}, nil).Once()
231- repos.EXPECT().View(mock.Anything, a).Return(app.RepoView{Repo: a}, nil).Once()
232- repos.EXPECT().View(mock.Anything, b).Return(app.RepoView{Repo: b}, nil).Once()
230+ repos.EXPECT().ListPage(mock.Anything, domain.Owner(), 2, "tok").Return(ports.RepoPage{Repos: []domain.Repo{a, b}, NextPageToken: "next"}, nil).Once()
231+ repos.EXPECT().View(mock.Anything, a).Return(ports.RepoView{Repo: a}, nil).Once()
232+ repos.EXPECT().View(mock.Anything, b).Return(ports.RepoView{Repo: b}, nil).Once()
233233 resp, err := (&repoService{repos}).ListRepos(ownerCtx(), connect.NewRequest(&pb.ListReposRequest{PageSize: 2, PageToken: "tok"}))
234234 require.NoError(t, err)
235235 require.Len(t, resp.Msg.GetRepos(), 2)
@@ -245,8 +245,8 @@ func TestRepoServiceListRepos(t *testing.T) {
245245 } {
246246 t.Run(name, func(t *testing.T) {
247247 t.Parallel()
248- repos := mocks.NewRepos(t)
249- repos.EXPECT().ListPage(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(app.RepoPage{}, tc.err).Once()
248+ repos := mocks.NewRepoService(t)
249+ repos.EXPECT().ListPage(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(ports.RepoPage{}, tc.err).Once()
250250 _, err := (&repoService{repos}).ListRepos(ownerCtx(), connect.NewRequest(&pb.ListReposRequest{}))
251251 requireCode(t, tc.code, err)
252252 })
M internal/adapters/rpc/server.go +2 -1
@@ -9,12 +9,13 @@ import (
99 "connectrpc.com/connect"
1010
1111 pb "bnema.dev/ermite-api/gen/ermite"
12+ "bnema.dev/ermite/internal/ports"
1213 )
1314
1415 // serverService implements ermiteconnect.ServerServiceHandler.
1516 type serverService struct {
1617 version string
17- key SigningKey
18+ key ports.SigningKeyService
1819 }
1920
2021 func (s *serverService) GetVersion(context.Context, *connect.Request[pb.GetVersionRequest]) (*connect.Response[pb.GetVersionResponse], error) {
M internal/adapters/rpc/server_test.go +3 -3
@@ -11,13 +11,13 @@ import (
1111 "github.com/stretchr/testify/require"
1212
1313 pb "bnema.dev/ermite-api/gen/ermite"
14- "bnema.dev/ermite/internal/adapters/rpc/mocks"
1514 "bnema.dev/ermite/internal/domain"
15+ "bnema.dev/ermite/internal/ports/mocks"
1616 )
1717
1818 func TestServerServiceGetVersion(t *testing.T) {
1919 t.Parallel()
20- svc := &serverService{version: "v1.2.3", key: mocks.NewSigningKey(t)}
20+ svc := &serverService{version: "v1.2.3", key: mocks.NewSigningKeyService(t)}
2121 resp, err := svc.GetVersion(context.Background(), connect.NewRequest(&pb.GetVersionRequest{}))
2222 require.NoError(t, err)
2323 assert.Equal(t, "v1.2.3", resp.Msg.GetVersion())
@@ -30,7 +30,7 @@ func TestServerServiceGetVersion(t *testing.T) {
3030
3131 func TestServerServiceGetSigningKey(t *testing.T) {
3232 t.Parallel()
33- key := mocks.NewSigningKey(t)
33+ key := mocks.NewSigningKeyService(t)
3434 key.EXPECT().Info().Return(domain.SigningKeyInfo{
3535 Key: domain.SigningPublicKey{AuthorizedKey: "ssh-ed25519 AAAA", Fingerprint: "SHA256:xyz"},
3636 Committer: domain.Identity{Name: "ermite", Email: "ermite@example.com"},
D internal/adapters/rpc/services.go +0 -81
@@ -1,81 +0,0 @@
1-// SPDX-License-Identifier: AGPL-3.0-or-later
2-
3-package rpc
4-
5-import (
6- "context"
7- "io"
8- "time"
9-
10- "bnema.dev/ermite/internal/app"
11- "bnema.dev/ermite/internal/domain"
12-)
13-
14-// The interfaces below are what the handlers need of the use cases, declared
15-// where they are used. The types of app fit them; the mocks in rpc/mocks
16-// stand in for them in tests.
17-
18-// Repos is the repository use cases the API serves. *app.Repos fits.
19-type Repos interface {
20- ListPage(ctx context.Context, p domain.Principal, size int, token string) (app.RepoPage, error)
21- View(ctx context.Context, repo domain.Repo) (app.RepoView, error)
22- Get(ctx context.Context, p domain.Principal, name domain.RepoName) (domain.Repo, error)
23- Create(ctx context.Context, p domain.Principal, params app.CreateParams) (domain.Repo, error)
24- Update(ctx context.Context, p domain.Principal, name domain.RepoName, params app.UpdateParams) (domain.Repo, error)
25- Delete(ctx context.Context, p domain.Principal, name domain.RepoName) error
26-}
27-
28-// Tokens is the access token use cases the API serves. *app.Tokens fits.
29-type Tokens interface {
30- Create(ctx context.Context, p domain.Principal, params app.CreateTokenParams) (app.CreatedToken, error)
31- ListPage(ctx context.Context, p domain.Principal, size int, token string) (app.TokenPage, error)
32- Revoke(ctx context.Context, p domain.Principal, id string) error
33-}
34-
35-// Browse is the read side of a repository's content. *app.Browse fits.
36-type Browse interface {
37- ListRefs(ctx context.Context, p domain.Principal, repo domain.RepoName, params app.ListRefsParams) (app.RefPage, error)
38- ResolveRevision(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision) (domain.ResolvedRevision, error)
39- GetTree(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision, path domain.TreePath, includeLastCommit bool) (domain.Tree, error)
40- OpenBlob(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision, path domain.TreePath) (domain.BlobInfo, io.ReadCloser, error)
41- ListCommits(ctx context.Context, p domain.Principal, repo domain.RepoName, params app.ListCommitsParams) (app.CommitPage, error)
42- GetCommit(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision) (domain.CommitDetail, error)
43-}
44-
45-// Events streams the events of the forge. *app.Events fits.
46-type Events interface {
47- Watch(ctx context.Context, p domain.Principal, params app.WatchParams, send func(app.WatchEvent) error) error
48-}
49-
50-// MergeRequests is the merge request use cases. *app.MergeRequests fits.
51-type MergeRequests interface {
52- Create(ctx context.Context, p domain.Principal, name domain.RepoName, params app.CreateMergeRequestParams) (domain.MergeRequest, error)
53- List(ctx context.Context, p domain.Principal, name domain.RepoName, params app.ListMergeRequestsParams) (app.MergeRequestPage, error)
54- Get(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, sinceLastRead bool) (app.MergeRequestDetail, error)
55- AddComment(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params app.AddCommentParams) (domain.Comment, error)
56- ResolveComment(ctx context.Context, p domain.Principal, name domain.RepoName, number, commentID int64, resolved bool) (domain.Comment, error)
57- Close(ctx context.Context, p domain.Principal, name domain.RepoName, number int64) (domain.MergeRequest, error)
58- MarkRead(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, head domain.ObjectID) (domain.ObjectID, error)
59- Merge(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params app.MergeParams) (app.MergeResult, error)
60-}
61-
62-// SigningKey tells which key signs the commits of the forge. *app.SigningKey
63-// fits.
64-type SigningKey interface {
65- Info() domain.SigningKeyInfo
66-}
67-
68-// Sessions is the owner's web session use cases the API serves.
69-// *app.Sessions fits.
70-type Sessions interface {
71- CreateLoginLink(ctx context.Context, p domain.Principal) (app.LoginLink, error)
72- SignOutEverywhere(ctx context.Context, p domain.Principal) error
73-}
74-
75-// FailureLimiter counts the failed authentications per client. It is shared
76-// with the other adapters that authenticate, so that a client has one budget
77-// of failures. *clientlimit.Limiter fits.
78-type FailureLimiter interface {
79- Blocked(ctx context.Context, addr string) (bool, time.Duration)
80- Fail(ctx context.Context, addr string)
81-}
M internal/adapters/rpc/session.go +2 -1
@@ -8,11 +8,12 @@ import (
88 "connectrpc.com/connect"
99
1010 pb "bnema.dev/ermite-api/gen/ermite"
11+ "bnema.dev/ermite/internal/ports"
1112 )
1213
1314 // sessionService implements ermiteconnect.SessionServiceHandler. The browser
1415 // never calls it: sessions come from the link, and the API ignores cookies.
15-type sessionService struct{ sessions Sessions }
16+type sessionService struct{ sessions ports.SessionService }
1617
1718 func (s *sessionService) CreateLoginLink(ctx context.Context, _ *connect.Request[pb.CreateLoginLinkRequest]) (*connect.Response[pb.CreateLoginLinkResponse], error) {
1819 link, err := s.sessions.CreateLoginLink(ctx, caller(ctx))
M internal/adapters/rpc/session_test.go +5 -5
@@ -12,9 +12,9 @@ import (
1212 "github.com/stretchr/testify/require"
1313
1414 pb "bnema.dev/ermite-api/gen/ermite"
15- "bnema.dev/ermite/internal/adapters/rpc/mocks"
16- "bnema.dev/ermite/internal/app"
1715 "bnema.dev/ermite/internal/domain"
16+ "bnema.dev/ermite/internal/ports"
17+ "bnema.dev/ermite/internal/ports/mocks"
1818 )
1919
2020 func TestSessionServiceCreateLoginLink(t *testing.T) {
@@ -32,9 +32,9 @@ func TestSessionServiceCreateLoginLink(t *testing.T) {
3232 for _, tt := range tests {
3333 t.Run(tt.name, func(t *testing.T) {
3434 t.Parallel()
35- sessions := mocks.NewSessions(t)
35+ sessions := mocks.NewSessionService(t)
3636 sessions.EXPECT().CreateLoginLink(mock.Anything, domain.Owner()).
37- Return(app.LoginLink{URL: "https://x/-/login/n", ExpiresAt: exp}, tt.usecase).Once()
37+ Return(ports.LoginLink{URL: "https://x/-/login/n", ExpiresAt: exp}, tt.usecase).Once()
3838 resp, err := (&sessionService{sessions}).CreateLoginLink(ownerCtx(), connect.NewRequest(&pb.CreateLoginLinkRequest{}))
3939 if tt.wantCode != 0 {
4040 requireCode(t, tt.wantCode, err)
@@ -60,7 +60,7 @@ func TestSessionServiceSignOutEverywhere(t *testing.T) {
6060 for _, tt := range tests {
6161 t.Run(tt.name, func(t *testing.T) {
6262 t.Parallel()
63- sessions := mocks.NewSessions(t)
63+ sessions := mocks.NewSessionService(t)
6464 sessions.EXPECT().SignOutEverywhere(mock.Anything, domain.Owner()).Return(tt.usecase).Once()
6565 _, err := (&sessionService{sessions}).SignOutEverywhere(ownerCtx(), connect.NewRequest(&pb.SignOutEverywhereRequest{}))
6666 if tt.wantCode != 0 {
M internal/adapters/rpc/token.go +3 -3
@@ -8,15 +8,15 @@ import (
88 "connectrpc.com/connect"
99
1010 pb "bnema.dev/ermite-api/gen/ermite"
11- "bnema.dev/ermite/internal/app"
1211 "bnema.dev/ermite/internal/domain"
12+ "bnema.dev/ermite/internal/ports"
1313 )
1414
1515 // tokenService implements ermiteconnect.TokenServiceHandler.
16-type tokenService struct{ tokens Tokens }
16+type tokenService struct{ tokens ports.TokenService }
1717
1818 func (s *tokenService) CreateToken(ctx context.Context, req *connect.Request[pb.CreateTokenRequest]) (*connect.Response[pb.CreateTokenResponse], error) {
19- params := app.CreateTokenParams{
19+ params := ports.CreateTokenParams{
2020 Label: req.Msg.GetLabel(),
2121 ExpiresAt: goTime(req.Msg.GetExpiresAt()),
2222 }
M internal/adapters/rpc/token_test.go +11 -11
@@ -11,9 +11,9 @@ import (
1111 "github.com/stretchr/testify/require"
1212
1313 pb "bnema.dev/ermite-api/gen/ermite"
14- "bnema.dev/ermite/internal/adapters/rpc/mocks"
15- "bnema.dev/ermite/internal/app"
1614 "bnema.dev/ermite/internal/domain"
15+ "bnema.dev/ermite/internal/ports"
16+ "bnema.dev/ermite/internal/ports/mocks"
1717 )
1818
1919 func TestTokenServiceCreateToken(t *testing.T) {
@@ -35,10 +35,10 @@ func TestTokenServiceCreateToken(t *testing.T) {
3535 for _, tt := range tests {
3636 t.Run(tt.name, func(t *testing.T) {
3737 t.Parallel()
38- tokens := mocks.NewTokens(t)
38+ tokens := mocks.NewTokenService(t)
3939 if tt.wantCall {
4040 tokens.EXPECT().Create(mock.Anything, domain.Owner(), mock.Anything).
41- Return(app.CreatedToken{Token: domain.Token{ID: "t1", Label: "ci"}, Secret: "s3cret"}, tt.usecase).Once()
41+ Return(ports.CreatedToken{Token: domain.Token{ID: "t1", Label: "ci"}, Secret: "s3cret"}, tt.usecase).Once()
4242 }
4343 resp, err := (&tokenService{tokens}).CreateToken(ownerCtx(), connect.NewRequest(tt.req))
4444 if tt.wantCode != 0 {
@@ -54,10 +54,10 @@ func TestTokenServiceCreateToken(t *testing.T) {
5454
5555 func TestTokenServiceCreateTokenPassesParams(t *testing.T) {
5656 t.Parallel()
57- tokens := mocks.NewTokens(t)
58- tokens.EXPECT().Create(mock.Anything, domain.Owner(), mock.MatchedBy(func(p app.CreateTokenParams) bool {
57+ tokens := mocks.NewTokenService(t)
58+ tokens.EXPECT().Create(mock.Anything, domain.Owner(), mock.MatchedBy(func(p ports.CreateTokenParams) bool {
5959 return p.Label == "ci" && len(p.Scopes) == 1 && len(p.Repos) == 1 && p.Repos[0].String() == "demo"
60- })).Return(app.CreatedToken{}, nil).Once()
60+ })).Return(ports.CreatedToken{}, nil).Once()
6161 _, err := (&tokenService{tokens}).CreateToken(ownerCtx(), connect.NewRequest(&pb.CreateTokenRequest{
6262 Label: "ci", Scopes: []pb.Scope{pb.Scope_SCOPE_REPO_READ}, Repos: []string{"demo"},
6363 }))
@@ -78,7 +78,7 @@ func TestTokenServiceRevokeToken(t *testing.T) {
7878 for _, tt := range tests {
7979 t.Run(tt.name, func(t *testing.T) {
8080 t.Parallel()
81- tokens := mocks.NewTokens(t)
81+ tokens := mocks.NewTokenService(t)
8282 tokens.EXPECT().Revoke(mock.Anything, domain.Owner(), "t1").Return(tt.usecase).Once()
8383 _, err := (&tokenService{tokens}).RevokeToken(ownerCtx(), connect.NewRequest(&pb.RevokeTokenRequest{Id: "t1"}))
8484 if tt.wantCode != 0 {
@@ -92,15 +92,15 @@ func TestTokenServiceRevokeToken(t *testing.T) {
9292
9393 func TestTokenServiceListTokens(t *testing.T) {
9494 t.Parallel()
95- tokens := mocks.NewTokens(t)
95+ tokens := mocks.NewTokenService(t)
9696 tokens.EXPECT().ListPage(mock.Anything, domain.Owner(), 5, "tok").
97- Return(app.TokenPage{Tokens: []domain.Token{{ID: "a"}, {ID: "b"}}, NextPageToken: "n"}, nil).Once()
97+ Return(ports.TokenPage{Tokens: []domain.Token{{ID: "a"}, {ID: "b"}}, NextPageToken: "n"}, nil).Once()
9898 resp, err := (&tokenService{tokens}).ListTokens(ownerCtx(), connect.NewRequest(&pb.ListTokensRequest{PageSize: 5, PageToken: "tok"}))
9999 require.NoError(t, err)
100100 assert.Len(t, resp.Msg.GetTokens(), 2)
101101 assert.Equal(t, "n", resp.Msg.GetNextPageToken())
102102
103- tokens.EXPECT().ListPage(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(app.TokenPage{}, domain.ErrInvalidPageToken).Once()
103+ tokens.EXPECT().ListPage(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(ports.TokenPage{}, domain.ErrInvalidPageToken).Once()
104104 _, err = (&tokenService{tokens}).ListTokens(ownerCtx(), connect.NewRequest(&pb.ListTokensRequest{}))
105105 requireCode(t, connect.CodeInvalidArgument, err)
106106 }
M internal/adapters/web/events_test.go +6 -5
@@ -20,7 +20,8 @@ import (
2020
2121 pb "bnema.dev/ermite-api/gen/ermite"
2222 "bnema.dev/ermite-api/gen/ermite/ermiteconnect"
23- "bnema.dev/ermite/internal/adapters/web/mocks"
23+ "bnema.dev/ermite/internal/ports"
24+ "bnema.dev/ermite/internal/ports/mocks"
2425 )
2526
2627 func TestFormatEvent(t *testing.T) {
@@ -115,7 +116,7 @@ func eventsServer(t *testing.T, f *fakeEvents, beat time.Duration, writeTimeout
115116 }
116117
117118 // eventsServerWith is eventsServer with sessions, when sess is not nil.
118-func eventsServerWith(t *testing.T, f *fakeEvents, beat time.Duration, writeTimeout time.Duration, sess SessionAuth) *httptest.Server {
119+func eventsServerWith(t *testing.T, f *fakeEvents, beat time.Duration, writeTimeout time.Duration, sess ports.WebSessionService) *httptest.Server {
119120 t.Helper()
120121 api := http.NewServeMux()
121122 api.Handle(ermiteconnect.NewEventServiceHandler(f))
@@ -287,16 +288,16 @@ func TestRepoUpdatedReloadsSummaryAndRefs(t *testing.T) {
287288 }
288289 }
289290
290-func limiterFor(sess SessionAuth) FailureLimiter {
291+func limiterFor(sess ports.WebSessionService) ports.FailureLimiter {
291292 if sess == nil {
292293 return nil
293294 }
294295 return &countingLimiter{}
295296 }
296297
297-// newRevocable is a SessionAuth whose one session ends when the test sets
298+// newRevocable is a ports.WebSessionService whose one session ends when the test sets
298299 // ended, like SignOutEverywhere does.
299-func newRevocable(t *testing.T, valid string) (*mocks.SessionAuth, *atomic.Bool) {
300+func newRevocable(t *testing.T, valid string) (*mocks.WebSessionService, *atomic.Bool) {
300301 t.Helper()
301302 var ended atomic.Bool
302303 return newVerifier(t, valid, func(tok string) (bool, error) { return tok == valid && !ended.Load(), nil }), &ended
D internal/adapters/web/mocks/FailureLimiter.go +0 -160
@@ -1,160 +0,0 @@
1-// Code generated by mockery; DO NOT EDIT.
2-// github.com/vektra/mockery
3-// template: testify
4-
5-package mocks
6-
7-import (
8- "context"
9- "time"
10-
11- mock "github.com/stretchr/testify/mock"
12-)
13-
14-// NewFailureLimiter creates a new instance of FailureLimiter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
15-// The first argument is typically a *testing.T value.
16-func NewFailureLimiter(t interface {
17- mock.TestingT
18- Cleanup(func())
19-}) *FailureLimiter {
20- if helper, ok := t.(interface{ Helper() }); ok {
21- helper.Helper()
22- }
23-
24- mock := &FailureLimiter{}
25- mock.Mock.Test(t)
26-
27- t.Cleanup(func() {
28- if helper, ok := t.(interface{ Helper() }); ok {
29- helper.Helper()
30- }
31- mock.AssertExpectations(t)
32- })
33-
34- return mock
35-}
36-
37-// FailureLimiter is an autogenerated mock type for the FailureLimiter type
38-type FailureLimiter struct {
39- mock.Mock
40-}
41-
42-type FailureLimiter_Expecter struct {
43- mock *mock.Mock
44-}
45-
46-func (_m *FailureLimiter) EXPECT() *FailureLimiter_Expecter {
47- return &FailureLimiter_Expecter{mock: &_m.Mock}
48-}
49-
50-// Blocked provides a mock function for the type FailureLimiter
51-func (_mock *FailureLimiter) Blocked(ctx context.Context, addr string) (bool, time.Duration) {
52- ret := _mock.Called(ctx, addr)
53-
54- if len(ret) == 0 {
55- panic("no return value specified for Blocked")
56- }
57-
58- var r0 bool
59- var r1 time.Duration
60- if returnFunc, ok := ret.Get(0).(func(context.Context, string) (bool, time.Duration)); ok {
61- return returnFunc(ctx, addr)
62- }
63- if returnFunc, ok := ret.Get(0).(func(context.Context, string) bool); ok {
64- r0 = returnFunc(ctx, addr)
65- } else {
66- r0 = ret.Get(0).(bool)
67- }
68- if returnFunc, ok := ret.Get(1).(func(context.Context, string) time.Duration); ok {
69- r1 = returnFunc(ctx, addr)
70- } else {
71- r1 = ret.Get(1).(time.Duration)
72- }
73- return r0, r1
74-}
75-
76-// FailureLimiter_Blocked_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Blocked'
77-type FailureLimiter_Blocked_Call struct {
78- *mock.Call
79-}
80-
81-// Blocked is a helper method to define mock.On call
82-// - ctx context.Context
83-// - addr string
84-func (_e *FailureLimiter_Expecter) Blocked(ctx any, addr any) *FailureLimiter_Blocked_Call {
85- return &FailureLimiter_Blocked_Call{Call: _e.mock.On("Blocked", ctx, addr)}
86-}
87-
88-func (_c *FailureLimiter_Blocked_Call) Run(run func(ctx context.Context, addr string)) *FailureLimiter_Blocked_Call {
89- _c.Call.Run(func(args mock.Arguments) {
90- var arg0 context.Context
91- if args[0] != nil {
92- arg0 = args[0].(context.Context)
93- }
94- var arg1 string
95- if args[1] != nil {
96- arg1 = args[1].(string)
97- }
98- run(
99- arg0,
100- arg1,
101- )
102- })
103- return _c
104-}
105-
106-func (_c *FailureLimiter_Blocked_Call) Return(b bool, duration time.Duration) *FailureLimiter_Blocked_Call {
107- _c.Call.Return(b, duration)
108- return _c
109-}
110-
111-func (_c *FailureLimiter_Blocked_Call) RunAndReturn(run func(ctx context.Context, addr string) (bool, time.Duration)) *FailureLimiter_Blocked_Call {
112- _c.Call.Return(run)
113- return _c
114-}
115-
116-// Fail provides a mock function for the type FailureLimiter
117-func (_mock *FailureLimiter) Fail(ctx context.Context, addr string) {
118- _mock.Called(ctx, addr)
119- return
120-}
121-
122-// FailureLimiter_Fail_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Fail'
123-type FailureLimiter_Fail_Call struct {
124- *mock.Call
125-}
126-
127-// Fail is a helper method to define mock.On call
128-// - ctx context.Context
129-// - addr string
130-func (_e *FailureLimiter_Expecter) Fail(ctx any, addr any) *FailureLimiter_Fail_Call {
131- return &FailureLimiter_Fail_Call{Call: _e.mock.On("Fail", ctx, addr)}
132-}
133-
134-func (_c *FailureLimiter_Fail_Call) Run(run func(ctx context.Context, addr string)) *FailureLimiter_Fail_Call {
135- _c.Call.Run(func(args mock.Arguments) {
136- var arg0 context.Context
137- if args[0] != nil {
138- arg0 = args[0].(context.Context)
139- }
140- var arg1 string
141- if args[1] != nil {
142- arg1 = args[1].(string)
143- }
144- run(
145- arg0,
146- arg1,
147- )
148- })
149- return _c
150-}
151-
152-func (_c *FailureLimiter_Fail_Call) Return() *FailureLimiter_Fail_Call {
153- _c.Call.Return()
154- return _c
155-}
156-
157-func (_c *FailureLimiter_Fail_Call) RunAndReturn(run func(ctx context.Context, addr string)) *FailureLimiter_Fail_Call {
158- _c.Run(run)
159- return _c
160-}
M internal/adapters/web/session.go +3 -27
@@ -16,6 +16,7 @@ import (
1616 "github.com/bnema/zerowrap"
1717
1818 "bnema.dev/ermite/internal/clientlimit"
19+ "bnema.dev/ermite/internal/ports"
1920 )
2021
2122 // The owner's session on the web (ADR 0018).
@@ -39,31 +40,6 @@ const CookieName = "__Host-ermite"
3940 // loginPrefix is the path of login links, up to the nonce.
4041 const loginPrefix = "/-/login/"
4142
42-// ErrLinkRejected is what SessionAuth.Login returns for a link that is
43-// unknown, expired or already used.
44-var ErrLinkRejected = errors.New("login link rejected")
45-
46-// SessionAuth is the session side of the use cases, as the web needs it.
47-type SessionAuth interface {
48- // LoginLinkValid reports whether the link is pending, without using it.
49- LoginLinkValid(nonce string) bool
50- // Login uses the link and returns the session token and how long it
51- // lasts, or ErrLinkRejected.
52- Login(ctx context.Context, nonce string) (token string, lifetime time.Duration, err error)
53- // Verify checks a session token. When it is valid it returns the context
54- // to serve the request with, which carries the owner as the caller of the
55- // API; when it is not, ok is false and err nil. An error means the check
56- // could not be made (the request then runs as anonymous).
57- Verify(ctx context.Context, token string) (owner context.Context, ok bool, err error)
58-}
59-
60-// FailureLimiter counts failed logins per client, with the failed
61-// authentications of the other routes. *clientlimit.Limiter fits.
62-type FailureLimiter interface {
63- Blocked(ctx context.Context, addr string) (bool, time.Duration)
64- Fail(ctx context.Context, addr string)
65-}
66-
6743 type signedInKey struct{}
6844
6945 // SignedIn reports whether the request carries a valid owner session. The
@@ -275,12 +251,12 @@ func (s *Server) loginPost(w http.ResponseWriter, r *http.Request) {
275251 nonce := r.PathValue("nonce")
276252 var token string
277253 var lifetime time.Duration
278- err := ErrLinkRejected
254+ err := ports.ErrLinkRejected
279255 if validNonce(nonce) {
280256 token, lifetime, err = s.cfg.Sessions.Login(ctx, nonce)
281257 }
282258 switch {
283- case errors.Is(err, ErrLinkRejected):
259+ case errors.Is(err, ports.ErrLinkRejected):
284260 s.cfg.Limiter.Fail(ctx, addr)
285261 s.secretPage(w, r, http.StatusNotFound, s.loginRejectedPage())
286262 return
M internal/adapters/web/session_test.go +13 -12
@@ -17,21 +17,22 @@ import (
1717 "github.com/stretchr/testify/require"
1818
1919 "bnema.dev/ermite-api/gen/ermite/ermiteconnect"
20- "bnema.dev/ermite/internal/adapters/web/mocks"
20+ "bnema.dev/ermite/internal/ports"
21+ "bnema.dev/ermite/internal/ports/mocks"
2122 )
2223
2324 // nonceA is the one valid login link of the mocks.
2425 var nonceA = strings.Repeat("a", 43)
2526
26-// newVerifier is a SessionAuth that accepts the session token valid, checked
27+// newVerifier is a WebSessionService that accepts the session token valid, checked
2728 // by verify when it is not nil, and that does not know any login link. A
2829 // call that is not expected fails the test.
29-func newVerifier(t *testing.T, valid string, verify func(token string) (bool, error)) *mocks.SessionAuth {
30+func newVerifier(t *testing.T, valid string, verify func(token string) (bool, error)) *mocks.WebSessionService {
3031 t.Helper()
3132 if verify == nil {
3233 verify = func(token string) (bool, error) { return token == valid, nil }
3334 }
34- m := mocks.NewSessionAuth(t)
35+ m := mocks.NewWebSessionService(t)
3536 m.EXPECT().Verify(mock.Anything, mock.Anything).RunAndReturn(
3637 func(ctx context.Context, token string) (context.Context, bool, error) {
3738 ok, err := verify(token)
@@ -41,22 +42,22 @@ func newVerifier(t *testing.T, valid string, verify func(token string) (bool, er
4142 }
4243
4344 // expectLogin makes nonceA the one link of m, answering with the token valid.
44-func expectLogin(m *mocks.SessionAuth, valid string) {
45+func expectLogin(m *mocks.WebSessionService, valid string) {
4546 m.EXPECT().LoginLinkValid(mock.Anything).RunAndReturn(func(n string) bool { return n == nonceA }).Maybe()
4647 m.EXPECT().Login(mock.Anything, nonceA).Return(valid, time.Hour, nil).Maybe()
47- m.EXPECT().Login(mock.Anything, mock.Anything).Return("", time.Duration(0), ErrLinkRejected).Maybe()
48+ m.EXPECT().Login(mock.Anything, mock.Anything).Return("", time.Duration(0), ports.ErrLinkRejected).Maybe()
4849 }
4950
50-// newSessions is a SessionAuth with one session token and one login link.
51-func newSessions(t *testing.T, valid string) *mocks.SessionAuth {
51+// newSessions is a WebSessionService with one session token and one login link.
52+func newSessions(t *testing.T, valid string) *mocks.WebSessionService {
5253 t.Helper()
5354 m := newVerifier(t, valid, nil)
5455 expectLogin(m, valid)
5556 return m
5657 }
5758
58-// newBrokenSessions is a SessionAuth whose check cannot be made.
59-func newBrokenSessions(t *testing.T, valid string) *mocks.SessionAuth {
59+// newBrokenSessions is a WebSessionService whose check cannot be made.
60+func newBrokenSessions(t *testing.T, valid string) *mocks.WebSessionService {
6061 t.Helper()
6162 m := newVerifier(t, valid, func(string) (bool, error) { return false, errors.New("kv down") })
6263 expectLogin(m, valid)
@@ -68,7 +69,7 @@ type countingLimiter struct{ fails int }
6869 func (*countingLimiter) Blocked(context.Context, string) (bool, time.Duration) { return false, 0 }
6970 func (l *countingLimiter) Fail(context.Context, string) { l.fails++ }
7071
71-func newSessionServer(t *testing.T, sess SessionAuth, publicURL string) (*Server, *countingLimiter) {
72+func newSessionServer(t *testing.T, sess ports.WebSessionService, publicURL string) (*Server, *countingLimiter) {
7273 t.Helper()
7374 lim := &countingLimiter{}
7475 srv, err := New(Config{
@@ -294,7 +295,7 @@ func TestNoSessionsNoRoutes(t *testing.T) {
294295
295296 _, err = New(Config{
296297 Repos: failing{}, Git: ermiteconnect.NewGitServiceClient(http.DefaultClient, "http://unused.invalid"),
297- PublicURL: "https://forge.example", Sessions: mocks.NewSessionAuth(t),
298+ PublicURL: "https://forge.example", Sessions: mocks.NewWebSessionService(t),
298299 })
299300 assert.Error(t, err, "sessions need a limiter")
300301 }
M internal/adapters/web/web.go +3 -2
@@ -33,6 +33,7 @@ import (
3333 "github.com/bnema/zerowrap"
3434
3535 "bnema.dev/ermite-api/gen/ermite/ermiteconnect"
36+ "bnema.dev/ermite/internal/ports"
3637 )
3738
3839 // Patterns are the ServeMux patterns the handler serves: everything. The
@@ -69,10 +70,10 @@ type Config struct {
6970 Now func() time.Time
7071 // Sessions signs the owner in (ADR 0018). Without it there are no login
7172 // routes and every visitor is anonymous.
72- Sessions SessionAuth
73+ Sessions ports.WebSessionService
7374 // Limiter counts the failed logins; required with Sessions. Share it with
7475 // the API and Git, so that a client has one budget of failures.
75- Limiter FailureLimiter
76+ Limiter ports.FailureLimiter
7677 // TrustedProxies are the networks allowed to set X-Forwarded-For, to find
7778 // the address of the client that fails a login.
7879 TrustedProxies []netip.Prefix
M internal/app/browse.go +15 -52
@@ -31,6 +31,8 @@ type Browse struct {
3131 history *historyCache
3232 }
3333
34+var _ ports.BrowseService = (*Browse)(nil)
35+
3436 // NewBrowse builds the read use cases over their ports, with a history cache
3537 // of the default bounds.
3638 func NewBrowse(meta ports.RepoMetaStore, reader ports.RepoReader) *Browse {
@@ -71,23 +73,6 @@ func repoEpoch(r domain.Repo) int64 {
7173 return r.CreatedAt.UnixNano()
7274 }
7375
74-// ListRefsParams selects the refs to list.
75-type ListRefsParams struct {
76- // Kinds restricts the result; empty means every kind.
77- Kinds []domain.RefKind
78- // PageSize is 0 for the default, and lowered to MaxRefPageSize.
79- PageSize int
80- // PageToken is the NextPageToken of the previous page, or empty.
81- PageToken string
82-}
83-
84-// RefPage is a page of refs.
85-type RefPage struct {
86- Refs []domain.Ref
87- // NextPageToken is empty on the last page.
88- NextPageToken string
89-}
90-
9176 func kindRank(k domain.RefKind) int {
9277 switch k {
9378 case domain.RefKindHead:
@@ -101,26 +86,26 @@ func kindRank(k domain.RefKind) int {
10186 // ListRefs lists HEAD, the branches and the tags, in that order, then by
10287 // name. The page token names the last ref of the previous page, so refs that
10388 // appear or disappear between two pages never shift the others.
104-func (b *Browse) ListRefs(ctx context.Context, p domain.Principal, repo domain.RepoName, params ListRefsParams) (RefPage, error) {
89+func (b *Browse) ListRefs(ctx context.Context, p domain.Principal, repo domain.RepoName, params ports.ListRefsParams) (ports.RefPage, error) {
10590 size, err := pageSizeUpTo(params.PageSize, MaxRefPageSize)
10691 if err != nil {
107- return RefPage{}, err
92+ return ports.RefPage{}, err
10893 }
10994 for _, k := range params.Kinds {
11095 if k != domain.RefKindHead && k != domain.RefKindBranch && k != domain.RefKindTag {
111- return RefPage{}, fmt.Errorf("%w: %q", domain.ErrInvalidRefKind, k)
96+ return ports.RefPage{}, fmt.Errorf("%w: %q", domain.ErrInvalidRefKind, k)
11297 }
11398 }
11499 after, err := decodeRefToken(params.PageToken)
115100 if err != nil {
116- return RefPage{}, err
101+ return ports.RefPage{}, err
117102 }
118103 if _, err := b.authorize(ctx, p, repo); err != nil {
119- return RefPage{}, err
104+ return ports.RefPage{}, err
120105 }
121106 all, err := b.reader.ListRefs(ctx, repo)
122107 if err != nil {
123- return RefPage{}, err
108+ return ports.RefPage{}, err
124109 }
125110
126111 var wanted []domain.Ref
@@ -129,7 +114,7 @@ func (b *Browse) ListRefs(ctx context.Context, p domain.Principal, repo domain.R
129114 wanted = append(wanted, r)
130115 }
131116 }
132- var page RefPage
117+ var page ports.RefPage
133118 page.Refs, page.NextPageToken = pageAfter(wanted, size,
134119 func(r domain.Ref) bool { return after == nil || refAfter(r, *after) },
135120 encodeRefToken)
@@ -227,53 +212,31 @@ func (b *Browse) OpenBlob(ctx context.Context, p domain.Principal, repo domain.R
227212 return b.reader.OpenBlob(ctx, repo, rev, path)
228213 }
229214
230-// ListCommitsParams selects a page of history.
231-type ListCommitsParams struct {
232- // Revision is where the history starts; the zero value is HEAD. Ignored
233- // when PageToken is set: the token pins the commit the first page started
234- // from, so every page walks the same history even if a branch moves.
235- Revision domain.Revision
236- // Path limits the history to the commits that changed it. It must be the
237- // same on every page of a listing.
238- Path domain.TreePath
239- // PageSize is 0 for the default, and lowered to MaxPageSize.
240- PageSize int
241- // PageToken is the NextPageToken of the previous page, or empty.
242- PageToken string
243-}
244-
245-// CommitPage is a page of history, newest first.
246-type CommitPage struct {
247- Commits []domain.Commit
248- // NextPageToken is empty on the last page.
249- NextPageToken string
250-}
251-
252215 // ListCommits returns a page of history. The page token holds the commit the
253216 // listing started from, how many commits were already returned and a digest
254217 // of the path; a token used with another path is invalid.
255-func (b *Browse) ListCommits(ctx context.Context, p domain.Principal, repo domain.RepoName, params ListCommitsParams) (CommitPage, error) {
218+func (b *Browse) ListCommits(ctx context.Context, p domain.Principal, repo domain.RepoName, params ports.ListCommitsParams) (ports.HistoryPage, error) {
256219 size, err := pageSize(params.PageSize)
257220 if err != nil {
258- return CommitPage{}, err
221+ return ports.HistoryPage{}, err
259222 }
260223 q := ports.CommitQuery{Revision: params.Revision, Path: params.Path, Limit: size}
261224 if params.PageToken != "" {
262225 head, skip, err := decodeCommitToken(params.PageToken, params.Path)
263226 if err != nil {
264- return CommitPage{}, err
227+ return ports.HistoryPage{}, err
265228 }
266229 q.Revision, q.Skip = domain.RevisionOf(head), skip
267230 }
268231 r, err := b.authorize(ctx, p, repo)
269232 if err != nil {
270- return CommitPage{}, err
233+ return ports.HistoryPage{}, err
271234 }
272235 res, err := b.listCommits(ctx, r, q)
273236 if err != nil {
274- return CommitPage{}, err
237+ return ports.HistoryPage{}, err
275238 }
276- out := CommitPage{Commits: res.Commits}
239+ out := ports.HistoryPage{Commits: res.Commits}
277240 if res.HasMore {
278241 out.NextPageToken = encodeCommitToken(res.Head, q.Skip+len(res.Commits), params.Path)
279242 }
M internal/app/browse_test.go +24 -24
@@ -55,7 +55,7 @@ func browseOps() []browseOp {
5555 root := domain.TreePath{}
5656 return []browseOp{
5757 {"ListRefs", func(b *app.Browse, ctx context.Context, p domain.Principal, repo domain.RepoName) error {
58- _, err := b.ListRefs(ctx, p, repo, app.ListRefsParams{})
58+ _, err := b.ListRefs(ctx, p, repo, ports.ListRefsParams{})
5959 return err
6060 }, func(r *mocks.RepoReader) { r.EXPECT().ListRefs(mock.Anything, mock.Anything).Return(nil, nil) }},
6161 {"ResolveRevision", func(b *app.Browse, ctx context.Context, p domain.Principal, repo domain.RepoName) error {
@@ -87,7 +87,7 @@ func browseOps() []browseOp {
8787 Return(domain.BlobInfo{}, io.NopCloser(strings.NewReader("")), nil)
8888 }},
8989 {"ListCommits", func(b *app.Browse, ctx context.Context, p domain.Principal, repo domain.RepoName) error {
90- _, err := b.ListCommits(ctx, p, repo, app.ListCommitsParams{})
90+ _, err := b.ListCommits(ctx, p, repo, ports.ListCommitsParams{})
9191 return err
9292 }, func(r *mocks.RepoReader) {
9393 r.EXPECT().ListCommits(mock.Anything, mock.Anything, mock.Anything).Return(ports.CommitPage{}, nil)
@@ -214,7 +214,7 @@ func TestListRefsPagingAndKinds(t *testing.T) {
214214 tok := ""
215215 for pages := 0; ; pages++ {
216216 require.Less(t, pages, 10)
217- page, err := b.ListRefs(ctx, domain.Anonymous(), pub.Name, app.ListRefsParams{PageSize: 2, PageToken: tok})
217+ page, err := b.ListRefs(ctx, domain.Anonymous(), pub.Name, ports.ListRefsParams{PageSize: 2, PageToken: tok})
218218 require.NoError(t, err)
219219 got = append(got, names(page.Refs)...)
220220 if tok = page.NextPageToken; tok == "" {
@@ -226,19 +226,19 @@ func TestListRefsPagingAndKinds(t *testing.T) {
226226
227227 t.Run("a ref that appears between pages does not shift the others", func(t *testing.T) {
228228 b := setup(t, all[:4])
229- page, err := b.ListRefs(ctx, domain.Anonymous(), pub.Name, app.ListRefsParams{PageSize: 2})
229+ page, err := b.ListRefs(ctx, domain.Anonymous(), pub.Name, ports.ListRefsParams{PageSize: 2})
230230 require.NoError(t, err)
231231 require.Equal(t, []string{"HEAD", "refs/heads/a"}, names(page.Refs))
232232
233233 b2 := setup(t, append([]domain.Ref{all[0], ref(domain.RefKindBranch, "refs/heads/0new")}, all[1:4]...))
234- next, err := b2.ListRefs(ctx, domain.Anonymous(), pub.Name, app.ListRefsParams{PageSize: 2, PageToken: page.NextPageToken})
234+ next, err := b2.ListRefs(ctx, domain.Anonymous(), pub.Name, ports.ListRefsParams{PageSize: 2, PageToken: page.NextPageToken})
235235 require.NoError(t, err)
236236 assert.Equal(t, []string{"refs/heads/b", "refs/heads/c"}, names(next.Refs))
237237 })
238238
239239 t.Run("kinds filter", func(t *testing.T) {
240240 b := setup(t, all)
241- page, err := b.ListRefs(ctx, domain.Anonymous(), pub.Name, app.ListRefsParams{Kinds: []domain.RefKind{domain.RefKindTag}})
241+ page, err := b.ListRefs(ctx, domain.Anonymous(), pub.Name, ports.ListRefsParams{Kinds: []domain.RefKind{domain.RefKindTag}})
242242 require.NoError(t, err)
243243 assert.Equal(t, []string{"refs/tags/v1", "refs/tags/v2"}, names(page.Refs))
244244 assert.Empty(t, page.NextPageToken)
@@ -251,13 +251,13 @@ func TestListRefsPagingAndKinds(t *testing.T) {
251251 many = append(many, ref(domain.RefKindTag, fmt.Sprintf("refs/tags/t%04d", i)))
252252 }
253253 b := setup(t, many)
254- page, err := b.ListRefs(ctx, domain.Anonymous(), pub.Name, app.ListRefsParams{})
254+ page, err := b.ListRefs(ctx, domain.Anonymous(), pub.Name, ports.ListRefsParams{})
255255 require.NoError(t, err)
256256 assert.Len(t, page.Refs, app.DefaultPageSize)
257257 assert.NotEmpty(t, page.NextPageToken)
258258
259259 b = setup(t, many)
260- page, err = b.ListRefs(ctx, domain.Anonymous(), pub.Name, app.ListRefsParams{PageSize: 100000})
260+ page, err = b.ListRefs(ctx, domain.Anonymous(), pub.Name, ports.ListRefsParams{PageSize: 100000})
261261 require.NoError(t, err)
262262 assert.Len(t, page.Refs, app.MaxRefPageSize, "a page holds up to MaxRefPageSize refs")
263263 assert.NotEmpty(t, page.NextPageToken)
@@ -266,7 +266,7 @@ func TestListRefsPagingAndKinds(t *testing.T) {
266266 t.Run("bad input never reaches the reader", func(t *testing.T) {
267267 t.Parallel()
268268 f := newBrowseFixture(t)
269- for _, params := range []app.ListRefsParams{
269+ for _, params := range []ports.ListRefsParams{
270270 {PageSize: -1},
271271 {Kinds: []domain.RefKind{"nope"}},
272272 {PageToken: "!!!"},
@@ -275,11 +275,11 @@ func TestListRefsPagingAndKinds(t *testing.T) {
275275 _, err := f.browse.ListRefs(ctx, domain.Anonymous(), pub.Name, params)
276276 assert.Error(t, err, "%+v", params)
277277 }
278- _, err := f.browse.ListRefs(ctx, domain.Anonymous(), pub.Name, app.ListRefsParams{PageSize: -1})
278+ _, err := f.browse.ListRefs(ctx, domain.Anonymous(), pub.Name, ports.ListRefsParams{PageSize: -1})
279279 assert.ErrorIs(t, err, domain.ErrInvalidPageSize)
280- _, err = f.browse.ListRefs(ctx, domain.Anonymous(), pub.Name, app.ListRefsParams{Kinds: []domain.RefKind{"x"}})
280+ _, err = f.browse.ListRefs(ctx, domain.Anonymous(), pub.Name, ports.ListRefsParams{Kinds: []domain.RefKind{"x"}})
281281 assert.ErrorIs(t, err, domain.ErrInvalidRefKind)
282- _, err = f.browse.ListRefs(ctx, domain.Anonymous(), pub.Name, app.ListRefsParams{PageToken: "!!!"})
282+ _, err = f.browse.ListRefs(ctx, domain.Anonymous(), pub.Name, ports.ListRefsParams{PageToken: "!!!"})
283283 assert.ErrorIs(t, err, domain.ErrInvalidPageToken)
284284 })
285285 }
@@ -296,7 +296,7 @@ func TestListCommitsPaging(t *testing.T) {
296296 f.meta.EXPECT().Get(ctx, pub.Name).Return(pub, nil)
297297 f.reader.EXPECT().ListCommits(ctx, pub.Name, ports.CommitQuery{Path: p, Limit: 10}).
298298 Return(ports.CommitPage{Head: oid("a"), Commits: []domain.Commit{{ID: oid("b")}}, HasMore: true}, nil)
299- page, err := f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, app.ListCommitsParams{Path: p, PageSize: 10})
299+ page, err := f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, ports.ListCommitsParams{Path: p, PageSize: 10})
300300 require.NoError(t, err)
301301 require.Len(t, page.Commits, 1)
302302 require.NotEmpty(t, page.NextPageToken)
@@ -308,7 +308,7 @@ func TestListCommitsPaging(t *testing.T) {
308308 f.meta.EXPECT().Get(ctx, pub.Name).Return(pub, nil)
309309 // The revision of the request is ignored once there is a token.
310310 rev, _ := domain.ParseRevision("moved")
311- next, err := f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, app.ListCommitsParams{
311+ next, err := f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, ports.ListCommitsParams{
312312 Revision: rev, Path: p, PageSize: 10, PageToken: page.NextPageToken,
313313 })
314314 require.NoError(t, err)
@@ -322,10 +322,10 @@ func TestListCommitsPaging(t *testing.T) {
322322 f.meta.EXPECT().Get(ctx, pub.Name).Return(pub, nil)
323323 f.reader.EXPECT().ListCommits(ctx, pub.Name, mock.Anything).
324324 Return(ports.CommitPage{Head: oid("a"), Commits: []domain.Commit{{ID: oid("b")}}, HasMore: true}, nil)
325- page, err := f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, app.ListCommitsParams{Path: p})
325+ page, err := f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, ports.ListCommitsParams{Path: p})
326326 require.NoError(t, err)
327327
328- for _, params := range []app.ListCommitsParams{
328+ for _, params := range []ports.ListCommitsParams{
329329 {PageToken: page.NextPageToken}, // no path
330330 {Path: mustPath(t, "other"), PageToken: page.NextPageToken}, // another path
331331 {Path: p, PageToken: "garbage"},
@@ -343,7 +343,7 @@ func TestListCommitsPaging(t *testing.T) {
343343 f.meta.EXPECT().Get(ctx, pub.Name).Return(pub, nil).Maybe()
344344 f.reader.EXPECT().ListCommits(ctx, pub.Name, mock.Anything).
345345 Return(ports.CommitPage{Head: oid("a"), Commits: []domain.Commit{{ID: oid("b")}}, HasMore: true}, nil).Once()
346- page, err := f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, app.ListCommitsParams{Path: p})
346+ page, err := f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, ports.ListCommitsParams{Path: p})
347347 require.NoError(t, err)
348348 raw, err := base64.RawURLEncoding.DecodeString(page.NextPageToken)
349349 require.NoError(t, err)
@@ -356,9 +356,9 @@ func TestListCommitsPaging(t *testing.T) {
356356
357357 f.reader.EXPECT().ListCommits(ctx, pub.Name, mock.MatchedBy(func(q ports.CommitQuery) bool { return q.Skip == app.MaxCommitSkip })).
358358 Return(ports.CommitPage{Head: oid("a")}, nil).Once()
359- _, err = f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, app.ListCommitsParams{Path: p, PageToken: withSkip(app.MaxCommitSkip)})
359+ _, err = f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, ports.ListCommitsParams{Path: p, PageToken: withSkip(app.MaxCommitSkip)})
360360 require.NoError(t, err, "the cap itself is allowed")
361- _, err = f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, app.ListCommitsParams{Path: p, PageToken: withSkip(app.MaxCommitSkip + 1)})
361+ _, err = f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, ports.ListCommitsParams{Path: p, PageToken: withSkip(app.MaxCommitSkip + 1)})
362362 assert.ErrorIs(t, err, domain.ErrInvalidPageToken)
363363 })
364364
@@ -368,11 +368,11 @@ func TestListCommitsPaging(t *testing.T) {
368368 f.meta.EXPECT().Get(ctx, pub.Name).Return(pub, nil).Times(2)
369369 f.reader.EXPECT().ListCommits(ctx, pub.Name, ports.CommitQuery{Limit: app.DefaultPageSize}).Return(ports.CommitPage{}, nil)
370370 f.reader.EXPECT().ListCommits(ctx, pub.Name, ports.CommitQuery{Limit: 100}).Return(ports.CommitPage{}, nil)
371- _, err := f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, app.ListCommitsParams{})
371+ _, err := f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, ports.ListCommitsParams{})
372372 require.NoError(t, err)
373- _, err = f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, app.ListCommitsParams{PageSize: 5000})
373+ _, err = f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, ports.ListCommitsParams{PageSize: 5000})
374374 require.NoError(t, err)
375- _, err = f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, app.ListCommitsParams{PageSize: -3})
375+ _, err = f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, ports.ListCommitsParams{PageSize: -3})
376376 assert.ErrorIs(t, err, domain.ErrInvalidPageSize)
377377 })
378378
@@ -382,10 +382,10 @@ func TestListCommitsPaging(t *testing.T) {
382382 f.meta.EXPECT().Get(ctx, priv.Name).Return(priv, nil)
383383 f.reader.EXPECT().ListCommits(ctx, priv.Name, mock.Anything).
384384 Return(ports.CommitPage{Head: oid("a"), Commits: []domain.Commit{{ID: oid("b")}}, HasMore: true}, nil)
385- page, err := f.browse.ListCommits(ctx, domain.Root(), priv.Name, app.ListCommitsParams{})
385+ page, err := f.browse.ListCommits(ctx, domain.Root(), priv.Name, ports.ListCommitsParams{})
386386 require.NoError(t, err)
387387 f.meta.EXPECT().Get(ctx, priv.Name).Return(priv, nil)
388- _, err = f.browse.ListCommits(ctx, domain.Anonymous(), priv.Name, app.ListCommitsParams{PageToken: page.NextPageToken})
388+ _, err = f.browse.ListCommits(ctx, domain.Anonymous(), priv.Name, ports.ListCommitsParams{PageToken: page.NextPageToken})
389389 assert.ErrorIs(t, err, domain.ErrNotFound)
390390 })
391391 }
M internal/app/browsecache_test.go +4 -4
@@ -131,7 +131,7 @@ func TestHistoryCacheNotServedAcrossRecreate(t *testing.T) {
131131 r.EXPECT().GetTree(mock.Anything, n, rev, dir, true).Return(domain.Tree{}, domain.ErrNotFound).Once()
132132 }},
133133 {"last change of a path", func(b *app.Browse) error {
134- _, err := b.ListCommits(ctx, domain.Anonymous(), n, app.ListCommitsParams{Revision: rev, Path: dir, PageSize: 1})
134+ _, err := b.ListCommits(ctx, domain.Anonymous(), n, ports.ListCommitsParams{Revision: rev, Path: dir, PageSize: 1})
135135 return err
136136 }, func(r *mocks.RepoReader) {
137137 q := ports.CommitQuery{Revision: rev, Path: dir, Limit: 1}
@@ -305,7 +305,7 @@ func TestListCommitsLastChangeCache(t *testing.T) {
305305 }, nil).Times(tc.calls)
306306 for range 2 {
307307 page, err := f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name,
308- app.ListCommitsParams{Revision: tc.rev, Path: tc.path, PageSize: tc.size})
308+ ports.ListCommitsParams{Revision: tc.rev, Path: tc.path, PageSize: tc.size})
309309 require.NoError(t, err)
310310 require.Len(t, page.Commits, 1)
311311 page.Commits[0].Parents[0] = oid("f") // must not leak into the cache
@@ -319,13 +319,13 @@ func TestListCommitsLastChangeCache(t *testing.T) {
319319 f.meta.EXPECT().Get(mock.Anything, pub.Name).Return(pub, nil)
320320 f.reader.EXPECT().ListCommits(mock.Anything, pub.Name, ports.CommitQuery{Revision: full, Path: file, Limit: 1}).
321321 Return(ports.CommitPage{Head: oid("a"), Commits: []domain.Commit{{ID: oid("c")}}, HasMore: true}, nil).Once()
322- first, err := f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, app.ListCommitsParams{Revision: full, Path: file, PageSize: 1})
322+ first, err := f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, ports.ListCommitsParams{Revision: full, Path: file, PageSize: 1})
323323 require.NoError(t, err)
324324 require.NotEmpty(t, first.NextPageToken)
325325 f.reader.EXPECT().ListCommits(mock.Anything, pub.Name, ports.CommitQuery{Revision: domain.RevisionOf(oid("a")), Path: file, Skip: 1, Limit: 1}).
326326 Return(ports.CommitPage{Head: oid("a")}, nil).Twice()
327327 for range 2 {
328- _, err = f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, app.ListCommitsParams{Path: file, PageSize: 1, PageToken: first.NextPageToken})
328+ _, err = f.browse.ListCommits(ctx, domain.Anonymous(), pub.Name, ports.ListCommitsParams{Path: file, PageSize: 1, PageToken: first.NextPageToken})
329329 require.NoError(t, err)
330330 }
331331 })
M internal/app/events.go +13 -45
@@ -27,7 +27,7 @@ const (
2727 // MaxWatchers is the most watches in progress at once.
2828 MaxWatchers = 256
2929 // MaxWatchersPerClient is the most watches in progress for one client
30- // address (see WatchParams.Client).
30+ // address (see ports.WatchParams.Client).
3131 MaxWatchersPerClient = 16
3232 // DefaultRevalidateEvery is how long a watcher with a stored token goes
3333 // without asking the token store whether the token still exists.
@@ -74,7 +74,7 @@ type bufferedEvent struct {
7474 // NewEvents and lives until Close.
7575 type Events struct {
7676 repos *Repos
77- auth Revalidator
77+ auth ports.Revalidator
7878 clock ports.Clock
7979 boot string
8080 size int
@@ -92,13 +92,15 @@ type Events struct {
9292 clients map[string]int // watches in progress by client
9393 }
9494
95+var _ ports.EventService = (*Events)(nil)
96+
9597 // EventsConfig configures Events.
9698 type EventsConfig struct {
9799 // Size is the number of events remembered; DefaultEventBuffer when not
98100 // positive.
99101 Size int
100102 // Auth confirms that the token of a watcher still exists. Required.
101- Auth Revalidator
103+ Auth ports.Revalidator
102104 // RevalidateEvery is how long the answer of Auth is kept for a watcher;
103105 // DefaultRevalidateEvery when zero, and negative to ask for every event.
104106 RevalidateEvery time.Duration
@@ -246,40 +248,6 @@ func (e *Events) parseID(id string) (uint64, bool) {
246248 return n, err == nil && n > 0
247249 }
248250
249-// WatchParams select the events of a watch.
250-type WatchParams struct {
251- // Topics are TopicHome or "repo:<name>"; empty means TopicHome.
252- Topics []string
253- // ResumeAfter is the id of the last event the caller saw; empty to
254- // receive only new events.
255- ResumeAfter string
256- // Client identifies where the watch comes from, for the limit per client
257- // (MaxWatchersPerClient): an opaque key such as a normalized address; the
258- // web passes its visitor's. Empty only for callers with no client, which
259- // count against MaxWatchers alone.
260- Client string
261- // Ready, when not nil, is called once the topics are valid and the watch
262- // is registered: events published from then on are not missed. Nothing is
263- // sent before it returns.
264- Ready func()
265-}
266-
267-// WatchEvent is one item of a watch.
268-type WatchEvent struct {
269- // ID identifies the event, for resuming. Empty on a gap.
270- ID string
271- // Gap tells that events were lost: the caller must reload what it shows.
272- // Event is nil then.
273- Gap bool
274- // Event is a domain.RepoCreated, RepoUpdated, RepoDeleted, PushEvent,
275- // MergeRequestOpened, MergeRequestMerged, MergeRequestClosed or
276- // CommentAdded.
277- Event domain.Event
278- // Repo is the state of the repository at delivery, for RepoCreated and
279- // RepoUpdated.
280- Repo *RepoView
281-}
282-
283251 // watch is the state of one Watch call.
284252 type watch struct {
285253 p domain.Principal
@@ -337,7 +305,7 @@ func (e *Events) parseTopics(ctx context.Context, p domain.Principal, topics []s
337305 // buffered, or was not issued by this run, a gap comes first and the oldest
338306 // buffered event follows. A watcher that falls more than the buffer behind
339307 // gets a gap too, and carries on with the oldest buffered event.
340-func (e *Events) Watch(ctx context.Context, p domain.Principal, params WatchParams, send func(WatchEvent) error) error {
308+func (e *Events) Watch(ctx context.Context, p domain.Principal, params ports.WatchParams, send func(ports.WatchEvent) error) error {
341309 w, err := e.parseTopics(ctx, p, params.Topics)
342310 if err != nil {
343311 return err
@@ -382,7 +350,7 @@ func (e *Events) Watch(ctx context.Context, p domain.Principal, params WatchPara
382350 log := zerowrap.FromCtx(ctx)
383351 log.Debug().Strs("topics", params.Topics).Bool("resume", params.ResumeAfter != "").Bool("gap", gap).Msg("watch started")
384352 if gap {
385- if err := send(WatchEvent{Gap: true}); err != nil {
353+ if err := send(ports.WatchEvent{Gap: true}); err != nil {
386354 return err
387355 }
388356 }
@@ -392,7 +360,7 @@ func (e *Events) Watch(ctx context.Context, p domain.Principal, params WatchPara
392360 }
393361 batch, changed, oldest, lost, closed := e.since(next)
394362 if lost {
395- if err := send(WatchEvent{Gap: true}); err != nil {
363+ if err := send(ports.WatchEvent{Gap: true}); err != nil {
396364 return err
397365 }
398366 next = oldest
@@ -482,15 +450,15 @@ func (e *Events) since(next uint64) (batch []bufferedEvent, changed <-chan struc
482450 }
483451
484452 // deliver sends the event if the watcher follows its topic and may see it.
485-func (e *Events) deliver(ctx context.Context, w *watch, be bufferedEvent, send func(WatchEvent) error) error {
453+func (e *Events) deliver(ctx context.Context, w *watch, be bufferedEvent, send func(ports.WatchEvent) error) error {
486454 if err := e.stillAllowed(ctx, w); err != nil {
487455 return err
488456 }
489- out := WatchEvent{ID: e.id(be.seq), Event: be.ev}
457+ out := ports.WatchEvent{ID: e.id(be.seq), Event: be.ev}
490458 switch ev := be.ev.(type) {
491459 case domain.EventGap:
492460 // The feeder lost events: tell every watcher.
493- return send(WatchEvent{Gap: true})
461+ return send(ports.WatchEvent{Gap: true})
494462 case domain.RepoDeleted:
495463 // The repository is gone: use the visibility it had.
496464 if !w.wants(ev.Repo) || !canReadAs(w.p, ev.Repo, ev.Visibility) {
@@ -523,7 +491,7 @@ func (e *Events) deliver(ctx context.Context, w *watch, be bufferedEvent, send f
523491 // The exception is a repository that became private: the watchers who could
524492 // read it before are told once, with its name and its new visibility only,
525493 // so that what they show can be removed.
526-func (e *Events) deliverRepo(ctx context.Context, w *watch, name domain.RepoName, withRepo bool, be bufferedEvent, out WatchEvent, send func(WatchEvent) error) error {
494+func (e *Events) deliverRepo(ctx context.Context, w *watch, name domain.RepoName, withRepo bool, be bufferedEvent, out ports.WatchEvent, send func(ports.WatchEvent) error) error {
527495 if !w.wants(name) || !be.known {
528496 return nil
529497 }
@@ -541,7 +509,7 @@ func (e *Events) deliverRepo(ctx context.Context, w *watch, name domain.RepoName
541509 }
542510 if !canReadAs(w.p, name, be.vis) {
543511 if up, ok := be.ev.(domain.RepoUpdated); ok && canReadAs(w.p, name, up.Previous) {
544- out.Repo = &RepoView{Repo: domain.Repo{Name: name, Visibility: up.Visibility}}
512+ out.Repo = &ports.RepoView{Repo: domain.Repo{Name: name, Visibility: up.Visibility}}
545513 return send(out)
546514 }
547515 return nil
M internal/app/events_test.go +60 -60
@@ -17,8 +17,8 @@ import (
1717 "github.com/stretchr/testify/require"
1818
1919 "bnema.dev/ermite/internal/app"
20- appmocks "bnema.dev/ermite/internal/app/mocks"
2120 "bnema.dev/ermite/internal/domain"
21+ "bnema.dev/ermite/internal/ports"
2222 "bnema.dev/ermite/internal/ports/mocks"
2323 )
2424
@@ -28,7 +28,7 @@ type eventRig struct {
2828 meta *mocks.RepoMetaStore
2929 git *mocks.GitRepos
3030 feed chan domain.Event
31- auth *appmocks.Revalidator
31+ auth *mocks.Revalidator
3232 revoked sync.Map // token IDs revoked by the test
3333 revalidations atomic.Int32 // calls to auth.Revalidate
3434 events *app.Events
@@ -46,7 +46,7 @@ func newEventRigConfig(t *testing.T, cfg app.EventsConfig) *eventRig {
4646 meta: mocks.NewRepoMetaStore(t),
4747 git: mocks.NewGitRepos(t),
4848 feed: make(chan domain.Event, 4096),
49- auth: appmocks.NewRevalidator(t),
49+ auth: mocks.NewRevalidator(t),
5050 }
5151 // Tokens are valid until the test revokes them.
5252 r.auth.EXPECT().Revalidate(mock.Anything, mock.Anything).RunAndReturn(func(_ context.Context, p domain.Principal) error {
@@ -95,14 +95,14 @@ func (r *eventRig) push(n domain.RepoName, i int) {
9595
9696 // watcher is a running Watch.
9797 type watcher struct {
98- got chan app.WatchEvent
98+ got chan ports.WatchEvent
9999 done chan error
100100 }
101101
102-func (r *eventRig) watch(ctx context.Context, p domain.Principal, params app.WatchParams) *watcher {
103- w := &watcher{got: make(chan app.WatchEvent, 4096), done: make(chan error, 1)}
102+func (r *eventRig) watch(ctx context.Context, p domain.Principal, params ports.WatchParams) *watcher {
103+ w := &watcher{got: make(chan ports.WatchEvent, 4096), done: make(chan error, 1)}
104104 go func() {
105- w.done <- r.events.Watch(ctx, p, params, func(e app.WatchEvent) error {
105+ w.done <- r.events.Watch(ctx, p, params, func(e ports.WatchEvent) error {
106106 w.got <- e
107107 return nil
108108 })
@@ -110,7 +110,7 @@ func (r *eventRig) watch(ctx context.Context, p domain.Principal, params app.Wat
110110 return w
111111 }
112112
113-func (w *watcher) next(t *testing.T) app.WatchEvent {
113+func (w *watcher) next(t *testing.T) ports.WatchEvent {
114114 t.Helper()
115115 synctest.Wait() // everything the event can reach has run
116116 select {
@@ -121,7 +121,7 @@ func (w *watcher) next(t *testing.T) app.WatchEvent {
121121 default:
122122 t.Fatal("no event")
123123 }
124- return app.WatchEvent{}
124+ return ports.WatchEvent{}
125125 }
126126
127127 // sentinelOID marks the push events that only prove the ones before them were
@@ -150,10 +150,10 @@ func TestWatchLiveAndFiltered(t *testing.T) {
150150 ctx, cancel := context.WithCancel(context.Background())
151151 defer cancel()
152152
153- anon := r.watch(ctx, domain.Anonymous(), app.WatchParams{})
154- root := r.watch(ctx, domain.Root(), app.WatchParams{Topics: []string{"home"}})
155- scoped := r.watch(ctx, tokenPrincipal(domain.ScopeRepoRead, "other"), app.WatchParams{})
156- onlyPriv := r.watch(ctx, domain.Root(), app.WatchParams{Topics: []string{"repo:priv"}})
153+ anon := r.watch(ctx, domain.Anonymous(), ports.WatchParams{})
154+ root := r.watch(ctx, domain.Root(), ports.WatchParams{Topics: []string{"home"}})
155+ scoped := r.watch(ctx, tokenPrincipal(domain.ScopeRepoRead, "other"), ports.WatchParams{})
156+ onlyPriv := r.watch(ctx, domain.Root(), ports.WatchParams{Topics: []string{"repo:priv"}})
157157 synctest.Wait()
158158 require.Equal(t, 4, r.events.Watchers())
159159
@@ -191,7 +191,7 @@ func TestWatchRechecksAccessPerEvent(t *testing.T) {
191191 })
192192 ctx, cancel := context.WithCancel(context.Background())
193193 defer cancel()
194- w := r.watch(ctx, domain.Anonymous(), app.WatchParams{})
194+ w := r.watch(ctx, domain.Anonymous(), ports.WatchParams{})
195195 synctest.Wait()
196196 require.Equal(t, 1, r.events.Watchers())
197197
@@ -217,7 +217,7 @@ func TestWatchResumeAndGap(t *testing.T) {
217217 ctx, cancel := context.WithCancel(context.Background())
218218 t.Cleanup(cancel)
219219
220- first := r.watch(ctx, domain.Root(), app.WatchParams{})
220+ first := r.watch(ctx, domain.Root(), ports.WatchParams{})
221221 synctest.Wait()
222222 require.Equal(t, 1, r.events.Watchers())
223223 // One at a time: this watcher must not fall behind the small ring.
@@ -235,7 +235,7 @@ func TestWatchResumeAndGap(t *testing.T) {
235235 r, _, ctx, ids := setup(t)
236236 // 20 events were taken, the buffer holds the last 8: ids[12] is the
237237 // oldest still present; ids[0] is long gone.
238- w := r.watch(ctx, domain.Root(), app.WatchParams{ResumeAfter: ids[0]})
238+ w := r.watch(ctx, domain.Root(), ports.WatchParams{ResumeAfter: ids[0]})
239239 gap := w.next(t)
240240 assert.True(t, gap.Gap)
241241 assert.Empty(t, gap.ID)
@@ -250,7 +250,7 @@ func TestWatchResumeAndGap(t *testing.T) {
250250 synctest.Test(t, func(t *testing.T) {
251251 r, _, ctx, _ := setup(t)
252252 for _, id := range []string{"nonsense", "zz-1", "-", "0-0"} {
253- w := r.watch(ctx, domain.Root(), app.WatchParams{ResumeAfter: id})
253+ w := r.watch(ctx, domain.Root(), ports.WatchParams{ResumeAfter: id})
254254 assert.True(t, w.next(t).Gap, id)
255255 }
256256 })
@@ -260,7 +260,7 @@ func TestWatchResumeAndGap(t *testing.T) {
260260 t.Parallel()
261261 synctest.Test(t, func(t *testing.T) {
262262 r, _, ctx, _ := setup(t)
263- w := r.watch(ctx, domain.Root(), app.WatchParams{ResumeAfter: "otherboot-999"})
263+ w := r.watch(ctx, domain.Root(), ports.WatchParams{ResumeAfter: "otherboot-999"})
264264 assert.True(t, w.next(t).Gap)
265265 })
266266 })
@@ -269,7 +269,7 @@ func TestWatchResumeAndGap(t *testing.T) {
269269 t.Parallel()
270270 synctest.Test(t, func(t *testing.T) {
271271 r, n, ctx, ids := setup(t)
272- w := r.watch(ctx, domain.Root(), app.WatchParams{ResumeAfter: ids[16]})
272+ w := r.watch(ctx, domain.Root(), ports.WatchParams{ResumeAfter: ids[16]})
273273 for _, want := range ids[17:] {
274274 e := w.next(t)
275275 assert.False(t, e.Gap)
@@ -287,7 +287,7 @@ func TestWatchResumeAndGap(t *testing.T) {
287287 t.Parallel()
288288 synctest.Test(t, func(t *testing.T) {
289289 r, n, ctx, ids := setup(t)
290- w := r.watch(ctx, domain.Root(), app.WatchParams{ResumeAfter: ids[len(ids)-1]})
290+ w := r.watch(ctx, domain.Root(), ports.WatchParams{ResumeAfter: ids[len(ids)-1]})
291291 r.push(n, 99)
292292 e := w.next(t)
293293 assert.False(t, e.Gap)
@@ -302,7 +302,7 @@ func TestWatchTopicErrors(t *testing.T) {
302302 r.repo("pub", domain.VisibilityPublic)
303303 r.repo("priv", domain.VisibilityPrivate)
304304 r.meta.EXPECT().Get(mock.Anything, name(t, "gone")).Return(domain.Repo{}, domain.ErrNotFound).Maybe()
305- noop := func(app.WatchEvent) error { return nil }
305+ noop := func(ports.WatchEvent) error { return nil }
306306 ctx := context.Background()
307307
308308 for topic, want := range map[string]error{
@@ -313,7 +313,7 @@ func TestWatchTopicErrors(t *testing.T) {
313313 "repo:priv": domain.ErrNotFound, // hidden from anonymous
314314 "repo:pub:foo": domain.ErrInvalidName,
315315 } {
316- err := r.events.Watch(ctx, domain.Anonymous(), app.WatchParams{Topics: []string{topic}}, noop)
316+ err := r.events.Watch(ctx, domain.Anonymous(), ports.WatchParams{Topics: []string{topic}}, noop)
317317 assert.ErrorIs(t, err, want, topic)
318318 }
319319 }
@@ -325,7 +325,7 @@ func TestWatchEndsCleanly(t *testing.T) {
325325 synctest.Test(t, func(t *testing.T) {
326326 r := newEventRig(t, 0)
327327 ctx, cancel := context.WithCancel(context.Background())
328- w := r.watch(ctx, domain.Root(), app.WatchParams{})
328+ w := r.watch(ctx, domain.Root(), ports.WatchParams{})
329329 synctest.Wait()
330330 require.Equal(t, 1, r.events.Watchers())
331331 cancel()
@@ -344,7 +344,7 @@ func TestWatchEndsCleanly(t *testing.T) {
344344 t.Parallel()
345345 synctest.Test(t, func(t *testing.T) {
346346 r := newEventRig(t, 0)
347- w := r.watch(context.Background(), domain.Root(), app.WatchParams{})
347+ w := r.watch(context.Background(), domain.Root(), ports.WatchParams{})
348348 synctest.Wait()
349349 require.Equal(t, 1, r.events.Watchers())
350350 r.events.Close(context.Background())
@@ -367,7 +367,7 @@ func TestWatchEndsCleanly(t *testing.T) {
367367 boom := errors.New("client went away")
368368 done := make(chan error, 1)
369369 go func() {
370- done <- r.events.Watch(context.Background(), domain.Root(), app.WatchParams{}, func(app.WatchEvent) error { return boom })
370+ done <- r.events.Watch(context.Background(), domain.Root(), ports.WatchParams{}, func(ports.WatchEvent) error { return boom })
371371 }()
372372 synctest.Wait()
373373 require.Equal(t, 1, r.events.Watchers())
@@ -386,12 +386,12 @@ func TestWatchSlowWatcherGetsAGap(t *testing.T) {
386386
387387 release := make(chan struct{})
388388 var mu sync.Mutex
389- var got []app.WatchEvent
389+ var got []ports.WatchEvent
390390 done := make(chan error, 1)
391391 blocked := make(chan struct{})
392392 first := true
393393 go func() {
394- done <- r.events.Watch(context.Background(), domain.Root(), app.WatchParams{}, func(e app.WatchEvent) error {
394+ done <- r.events.Watch(context.Background(), domain.Root(), ports.WatchParams{}, func(e ports.WatchEvent) error {
395395 if first { // block on the first event while the ring turns over
396396 first = false
397397 close(blocked)
@@ -405,7 +405,7 @@ func TestWatchSlowWatcherGetsAGap(t *testing.T) {
405405 }()
406406 // A second, fast watcher tells when the use case has taken every event:
407407 // it receives them in order.
408- fast := r.watch(context.Background(), domain.Root(), app.WatchParams{})
408+ fast := r.watch(context.Background(), domain.Root(), ports.WatchParams{})
409409 synctest.Wait()
410410 require.Equal(t, 2, r.events.Watchers())
411411 r.push(n, 0)
@@ -444,8 +444,8 @@ func TestEventsRepoPayloads(t *testing.T) {
444444 priv := r.repo("priv", domain.VisibilityPrivate)
445445 ctx, cancel := context.WithCancel(context.Background())
446446 defer cancel()
447- anon := r.watch(ctx, domain.Anonymous(), app.WatchParams{})
448- root := r.watch(ctx, domain.Root(), app.WatchParams{})
447+ anon := r.watch(ctx, domain.Anonymous(), ports.WatchParams{})
448+ root := r.watch(ctx, domain.Root(), ports.WatchParams{})
449449 synctest.Wait()
450450 require.Equal(t, 2, r.events.Watchers())
451451
@@ -493,7 +493,7 @@ func TestWatchEndsWhenTheTokenIsRevoked(t *testing.T) {
493493 ctx, cancel := context.WithCancel(context.Background())
494494 defer cancel()
495495
496- w := r.watch(ctx, p, app.WatchParams{})
496+ w := r.watch(ctx, p, ports.WatchParams{})
497497 synctest.Wait()
498498 require.Equal(t, 1, r.events.Watchers())
499499 r.push(pub, 1)
@@ -523,7 +523,7 @@ func TestWatchEndsWhenTheTokenExpires(t *testing.T) {
523523 pub := r.repo("pub", domain.VisibilityPublic)
524524 // The rig's clock stands at now: this token expired at now.
525525 p := tokenWith("tok", now)
526- w := r.watch(context.Background(), p, app.WatchParams{})
526+ w := r.watch(context.Background(), p, ports.WatchParams{})
527527 r.push(pub, 1) // whether or not the watch had started, it ends unauthenticated
528528 synctest.Wait()
529529 select {
@@ -542,7 +542,7 @@ func TestWatchRevalidatesIdleWatchersAndCachesTheAnswer(t *testing.T) {
542542 t.Parallel()
543543 synctest.Test(t, func(t *testing.T) {
544544 r := newEventRigConfig(t, app.EventsConfig{}) // the production default interval
545- w := r.watch(context.Background(), tokenWith("tok", time.Time{}), app.WatchParams{})
545+ w := r.watch(context.Background(), tokenWith("tok", time.Time{}), ports.WatchParams{})
546546 synctest.Wait()
547547 require.Equal(t, 1, r.events.Watchers())
548548 r.revoked.Store("tok", true)
@@ -569,7 +569,7 @@ func TestWatchRevalidatesIdleWatchersAndCachesTheAnswer(t *testing.T) {
569569 synctest.Test(t, func(t *testing.T) {
570570 r := newEventRigConfig(t, app.EventsConfig{RevalidateEvery: time.Hour})
571571 pub := r.repo("pub", domain.VisibilityPublic)
572- w := r.watch(context.Background(), tokenWith("tok", time.Time{}), app.WatchParams{})
572+ w := r.watch(context.Background(), tokenWith("tok", time.Time{}), ports.WatchParams{})
573573 synctest.Wait()
574574 require.Equal(t, 1, r.events.Watchers())
575575 for i := range 20 {
@@ -592,8 +592,8 @@ func TestWatchRevalidatesIdleWatchersAndCachesTheAnswer(t *testing.T) {
592592 synctest.Test(t, func(t *testing.T) {
593593 r := newEventRigConfig(t, app.EventsConfig{RevalidateEvery: -1})
594594 pub := r.repo("pub", domain.VisibilityPublic)
595- a := r.watch(context.Background(), domain.Anonymous(), app.WatchParams{})
596- b := r.watch(context.Background(), domain.Root(), app.WatchParams{})
595+ a := r.watch(context.Background(), domain.Anonymous(), ports.WatchParams{})
596+ b := r.watch(context.Background(), domain.Root(), ports.WatchParams{})
597597 synctest.Wait()
598598 require.Equal(t, 2, r.events.Watchers())
599599 r.push(pub, 1)
@@ -606,7 +606,7 @@ func TestWatchRevalidatesIdleWatchersAndCachesTheAnswer(t *testing.T) {
606606
607607 func TestWatchLimits(t *testing.T) {
608608 t.Parallel()
609- noop := func(app.WatchEvent) error { return nil }
609+ noop := func(ports.WatchEvent) error { return nil }
610610
611611 t.Run("too many topics", func(t *testing.T) {
612612 t.Parallel()
@@ -619,8 +619,8 @@ func TestWatchLimits(t *testing.T) {
619619 }
620620 ctx, cancel := context.WithCancel(context.Background())
621621 cancel() // returns at once once the topics are accepted
622- assert.NoError(t, r.events.Watch(ctx, domain.Root(), app.WatchParams{Topics: ok}, noop))
623- err := r.events.Watch(context.Background(), domain.Root(), app.WatchParams{Topics: append(ok, "home")}, noop)
622+ assert.NoError(t, r.events.Watch(ctx, domain.Root(), ports.WatchParams{Topics: ok}, noop))
623+ err := r.events.Watch(context.Background(), domain.Root(), ports.WatchParams{Topics: append(ok, "home")}, noop)
624624 assert.ErrorIs(t, err, domain.ErrInvalid)
625625 })
626626 })
@@ -632,11 +632,11 @@ func TestWatchLimits(t *testing.T) {
632632 ctx, cancel := context.WithCancel(context.Background())
633633 defer cancel()
634634 for range app.MaxWatchers {
635- r.watch(ctx, domain.Root(), app.WatchParams{})
635+ r.watch(ctx, domain.Root(), ports.WatchParams{})
636636 }
637637 synctest.Wait()
638638 require.Equal(t, app.MaxWatchers, r.events.Watchers())
639- err := r.events.Watch(ctx, domain.Root(), app.WatchParams{}, noop)
639+ err := r.events.Watch(ctx, domain.Root(), ports.WatchParams{}, noop)
640640 assert.ErrorIs(t, err, domain.ErrResourceExhausted)
641641 assert.Equal(t, app.MaxWatchers, r.events.Watchers(), "a refused watch is not counted")
642642
@@ -645,7 +645,7 @@ func TestWatchLimits(t *testing.T) {
645645 require.Equal(t, 0, r.events.Watchers())
646646 again, stop := context.WithCancel(context.Background())
647647 stop()
648- assert.NoError(t, r.events.Watch(again, domain.Root(), app.WatchParams{}, noop))
648+ assert.NoError(t, r.events.Watch(again, domain.Root(), ports.WatchParams{}, noop))
649649 })
650650 })
651651
@@ -656,17 +656,17 @@ func TestWatchLimits(t *testing.T) {
656656 ctx, cancel := context.WithCancel(context.Background())
657657 defer cancel()
658658 for range app.MaxWatchersPerClient {
659- r.watch(ctx, domain.Root(), app.WatchParams{Client: "198.51.100.1"})
659+ r.watch(ctx, domain.Root(), ports.WatchParams{Client: "198.51.100.1"})
660660 }
661661 synctest.Wait()
662662 require.Equal(t, app.MaxWatchersPerClient, r.events.Watchers())
663663
664- err := r.events.Watch(ctx, domain.Root(), app.WatchParams{Client: "198.51.100.1"}, noop)
664+ err := r.events.Watch(ctx, domain.Root(), ports.WatchParams{Client: "198.51.100.1"}, noop)
665665 assert.ErrorIs(t, err, domain.ErrResourceExhausted)
666666
667667 // Another client, and callers in process, are not affected.
668- r.watch(ctx, domain.Root(), app.WatchParams{Client: "198.51.100.2"})
669- r.watch(ctx, domain.Root(), app.WatchParams{})
668+ r.watch(ctx, domain.Root(), ports.WatchParams{Client: "198.51.100.2"})
669+ r.watch(ctx, domain.Root(), ports.WatchParams{})
670670 synctest.Wait()
671671 require.Equal(t, app.MaxWatchersPerClient+2, r.events.Watchers())
672672
@@ -675,7 +675,7 @@ func TestWatchLimits(t *testing.T) {
675675 require.Equal(t, 0, r.events.Watchers())
676676 again, stop := context.WithCancel(context.Background())
677677 stop()
678- assert.NoError(t, r.events.Watch(again, domain.Root(), app.WatchParams{Client: "198.51.100.1"}, noop), "the slots were released")
678+ assert.NoError(t, r.events.Watch(again, domain.Root(), ports.WatchParams{Client: "198.51.100.1"}, noop), "the slots were released")
679679 })
680680 })
681681 }
@@ -712,7 +712,7 @@ func TestEventsKeepTheVisibilityOfTheirTime(t *testing.T) {
712712 defer cancel()
713713 r := newEventRig(t, 0)
714714 l, n := r.liveRepo("secret", domain.VisibilityPrivate, now)
715- root := r.watch(ctx, domain.Root(), app.WatchParams{})
715+ root := r.watch(ctx, domain.Root(), ports.WatchParams{})
716716 synctest.Wait()
717717 require.Equal(t, 1, r.events.Watchers())
718718 r.push(n, 1)
@@ -728,7 +728,7 @@ func TestEventsKeepTheVisibilityOfTheirTime(t *testing.T) {
728728 // An anonymous caller that resumes with an id nobody issued gets what
729729 // is buffered, filtered: the public update and the public push, not the
730730 // pushes of the private era.
731- anon := r.watch(ctx, domain.Anonymous(), app.WatchParams{ResumeAfter: "bogus"})
731+ anon := r.watch(ctx, domain.Anonymous(), ports.WatchParams{ResumeAfter: "bogus"})
732732 assert.True(t, anon.next(t).Gap)
733733 upd := anon.next(t)
734734 require.IsType(t, domain.RepoUpdated{}, upd.Event)
@@ -747,7 +747,7 @@ func TestEventsKeepTheVisibilityOfTheirTime(t *testing.T) {
747747 r := newEventRig(t, 0)
748748 first := now.Add(-time.Hour)
749749 l, n := r.liveRepo("again", domain.VisibilityPublic, first)
750- root := r.watch(ctx, domain.Root(), app.WatchParams{})
750+ root := r.watch(ctx, domain.Root(), ports.WatchParams{})
751751 synctest.Wait()
752752 require.Equal(t, 1, r.events.Watchers())
753753 r.push(n, 1)
@@ -757,7 +757,7 @@ func TestEventsKeepTheVisibilityOfTheirTime(t *testing.T) {
757757 r.push(n, 2)
758758 root.next(t)
759759
760- anon := r.watch(ctx, domain.Anonymous(), app.WatchParams{ResumeAfter: "bogus"})
760+ anon := r.watch(ctx, domain.Anonymous(), ports.WatchParams{ResumeAfter: "bogus"})
761761 assert.True(t, anon.next(t).Gap)
762762 e := anon.next(t)
763763 require.IsType(t, domain.PushEvent{}, e.Event)
@@ -773,10 +773,10 @@ func TestEventsKeepTheVisibilityOfTheirTime(t *testing.T) {
773773 defer cancel()
774774 r := newEventRig(t, 0)
775775 l, n := r.liveRepo("hide", domain.VisibilityPublic, now)
776- anon := r.watch(ctx, domain.Anonymous(), app.WatchParams{})
777- reader := r.watch(ctx, tokenPrincipal(domain.ScopeRepoRead, "hide"), app.WatchParams{})
778- other := r.watch(ctx, tokenPrincipal(domain.ScopeRepoRead, "elsewhere"), app.WatchParams{})
779- root := r.watch(ctx, domain.Root(), app.WatchParams{})
776+ anon := r.watch(ctx, domain.Anonymous(), ports.WatchParams{})
777+ reader := r.watch(ctx, tokenPrincipal(domain.ScopeRepoRead, "hide"), ports.WatchParams{})
778+ other := r.watch(ctx, tokenPrincipal(domain.ScopeRepoRead, "elsewhere"), ports.WatchParams{})
779+ root := r.watch(ctx, domain.Root(), ports.WatchParams{})
780780 synctest.Wait()
781781 require.Equal(t, 4, r.events.Watchers())
782782
@@ -809,7 +809,7 @@ func TestEventsKeepTheVisibilityOfTheirTime(t *testing.T) {
809809 defer cancel()
810810 r := newEventRig(t, 0)
811811 _, n := r.liveRepo("closed", domain.VisibilityPrivate, now)
812- anon := r.watch(ctx, domain.Anonymous(), app.WatchParams{})
812+ anon := r.watch(ctx, domain.Anonymous(), ports.WatchParams{})
813813 synctest.Wait()
814814 require.Equal(t, 1, r.events.Watchers())
815815 r.feed <- domain.RepoUpdated{Repo: n, At: now, Visibility: domain.VisibilityPrivate, Previous: domain.VisibilityPrivate, CreatedAt: now}
@@ -842,8 +842,8 @@ func TestPushEventsKeepTheirSnapshot(t *testing.T) {
842842 defer cancel()
843843 r := newEventRig(t, 0)
844844 l, n := r.liveRepo("secret", domain.VisibilityPrivate, now)
845- anon := r.watch(ctx, domain.Anonymous(), app.WatchParams{})
846- root := r.watch(ctx, domain.Root(), app.WatchParams{})
845+ anon := r.watch(ctx, domain.Anonymous(), ports.WatchParams{})
846+ root := r.watch(ctx, domain.Root(), ports.WatchParams{})
847847 synctest.Wait()
848848 require.Equal(t, 2, r.events.Watchers())
849849
@@ -859,7 +859,7 @@ func TestPushEventsKeepTheirSnapshot(t *testing.T) {
859859
860860 anon.quiet(t, r, r.repo("open", domain.VisibilityPublic))
861861
862- late := r.watch(ctx, domain.Anonymous(), app.WatchParams{ResumeAfter: "bogus"})
862+ late := r.watch(ctx, domain.Anonymous(), ports.WatchParams{ResumeAfter: "bogus"})
863863 assert.True(t, late.next(t).Gap)
864864 late.quiet(t, r, r.repo("open2", domain.VisibilityPublic))
865865 })
@@ -873,7 +873,7 @@ func TestPushEventsKeepTheirSnapshot(t *testing.T) {
873873 defer cancel()
874874 r := newEventRig(t, 0)
875875 n := r.repo("ghost", domain.VisibilityPublic)
876- root := r.watch(ctx, domain.Root(), app.WatchParams{})
876+ root := r.watch(ctx, domain.Root(), ports.WatchParams{})
877877 synctest.Wait()
878878 require.Equal(t, 1, r.events.Watchers())
879879 r.feed <- domain.PushEvent{Repo: n, Visibility: domain.VisibilityPublic, At: now}
M internal/app/maintenance.go +2 -0
@@ -38,6 +38,8 @@ type Maintainer struct {
3838 wg sync.WaitGroup
3939 }
4040
41+var _ ports.MaintenanceScheduler = (*Maintainer)(nil)
42+
4143 type maintEntry struct {
4244 timer *time.Timer // pending run, nil while running or idle
4345 running bool
M internal/app/mergerequests.go +29 -102
@@ -47,12 +47,14 @@ type MergeRequests struct {
4747 committer domain.Identity
4848 bus ports.EventBus
4949 clock ports.Clock
50- maint MaintenanceScheduler
50+ maint ports.MaintenanceScheduler
5151
5252 mu sync.Mutex
5353 locks map[domain.RepoName]chan struct{}
5454 }
5555
56+var _ ports.MergeRequestService = (*MergeRequests)(nil)
57+
5658 // MergeRequestsConfig holds what the merge request use cases need. Every field
5759 // is required.
5860 type MergeRequestsConfig struct {
@@ -68,7 +70,7 @@ type MergeRequestsConfig struct {
6870 Bus ports.EventBus
6971 Clock ports.Clock
7072 // Maint queues the maintenance that follows a merge, as it does a push.
71- Maint MaintenanceScheduler
73+ Maint ports.MaintenanceScheduler
7274 }
7375
7476 // NewMergeRequests builds the merge request use cases.
@@ -161,20 +163,12 @@ func (m *MergeRequests) branchOrZero(ctx context.Context, repo domain.RepoName,
161163 return id, err
162164 }
163165
164-// CreateMergeRequestParams describes the merge request to open.
165-type CreateMergeRequestParams struct {
166- Title string
167- Description string
168- // Source is required; Target defaults to the default branch.
169- Source, Target string
170-}
171-
172166 // Create opens a merge request and publishes MergeRequestOpened. The principal
173167 // needs mr:write. A request that breaks a rule of ADR 0017 (the texts, a
174168 // branch that does not exist, source and target the same) is rejected whole
175169 // with an *domain.InvalidFieldsError naming the fields. An open request with
176170 // the same source and target gives domain.ErrAlreadyExists.
177-func (m *MergeRequests) Create(ctx context.Context, p domain.Principal, name domain.RepoName, params CreateMergeRequestParams) (domain.MergeRequest, error) {
171+func (m *MergeRequests) Create(ctx context.Context, p domain.Principal, name domain.RepoName, params ports.CreateMergeRequestParams) (domain.MergeRequest, error) {
178172 repo, err := m.writable(ctx, p, name, domain.ScopeMRWrite)
179173 if err != nil {
180174 return domain.MergeRequest{}, err
@@ -239,7 +233,7 @@ func (m *MergeRequests) Create(ctx context.Context, p domain.Principal, name dom
239233 // Only a branch that does not exist is a violation. Any other failure to read
240234 // Git is returned as an error, so that it is logged and masked, not shown in
241235 // the field violations.
242-func (m *MergeRequests) checkBranches(ctx context.Context, name domain.RepoName, params CreateMergeRequestParams, v *domain.Violations) (source, target string, err error) {
236+func (m *MergeRequests) checkBranches(ctx context.Context, name domain.RepoName, params ports.CreateMergeRequestParams, v *domain.Violations) (source, target string, err error) {
243237 source, target = params.Source, params.Target
244238 if source == "" {
245239 v.Add("source_branch", "is required")
@@ -279,44 +273,27 @@ func (m *MergeRequests) checkBranches(ctx context.Context, name domain.RepoName,
279273 return source, target, nil
280274 }
281275
282-// ListMergeRequestsParams selects a page of merge requests.
283-type ListMergeRequestsParams struct {
284- // State restricts the result; empty means every state.
285- State domain.MRState
286- // PageSize is 0 for the default, and lowered to MaxPageSize.
287- PageSize int
288- // PageToken is the NextPageToken of the previous page, or empty.
289- PageToken string
290-}
291-
292-// MergeRequestPage is a page of merge requests, highest number first.
293-type MergeRequestPage struct {
294- MergeRequests []domain.MergeRequest
295- // NextPageToken is empty on the last page.
296- NextPageToken string
297-}
298-
299276 // List returns a page of the merge requests of a repository, highest number
300277 // first. The open ones carry the head of their source branch. The page token
301278 // names the last number of the previous page, so requests created between two
302279 // pages never shift the others.
303-func (m *MergeRequests) List(ctx context.Context, p domain.Principal, name domain.RepoName, params ListMergeRequestsParams) (MergeRequestPage, error) {
280+func (m *MergeRequests) List(ctx context.Context, p domain.Principal, name domain.RepoName, params ports.ListMergeRequestsParams) (ports.MergeRequestPage, error) {
304281 size, err := pageSize(params.PageSize)
305282 if err != nil {
306- return MergeRequestPage{}, err
283+ return ports.MergeRequestPage{}, err
307284 }
308285 after, err := decodeMRToken(params.PageToken)
309286 if err != nil {
310- return MergeRequestPage{}, err
287+ return ports.MergeRequestPage{}, err
311288 }
312289 if _, err := m.readable(ctx, p, name); err != nil {
313- return MergeRequestPage{}, err
290+ return ports.MergeRequestPage{}, err
314291 }
315292 all, err := m.store.List(ctx, name)
316293 if err != nil {
317- return MergeRequestPage{}, fmt.Errorf("list merge requests of %q: %w", name, err)
294+ return ports.MergeRequestPage{}, fmt.Errorf("list merge requests of %q: %w", name, err)
318295 }
319- var page MergeRequestPage
296+ var page ports.MergeRequestPage
320297 page.MergeRequests, page.NextPageToken = pageAfter(all, size,
321298 func(mr domain.MergeRequest) bool {
322299 return (params.State == "" || mr.State == params.State) && (after == 0 || mr.Number < after)
@@ -324,7 +301,7 @@ func (m *MergeRequests) List(ctx context.Context, p domain.Principal, name domai
324301 encodeMRToken)
325302 for i, mr := range page.MergeRequests {
326303 if page.MergeRequests[i], err = m.withHead(ctx, mr); err != nil {
327- return MergeRequestPage{}, err
304+ return ports.MergeRequestPage{}, err
328305 }
329306 }
330307 return page, nil
@@ -361,27 +338,6 @@ func (m *MergeRequests) get(ctx context.Context, p domain.Principal, name domain
361338 return repo, mr, nil
362339 }
363340
364-// MergeRequestDetail is a merge request with its review data.
365-type MergeRequestDetail struct {
366- MergeRequest domain.MergeRequest
367- // Commits are the commits of the head that the target lacks, newest
368- // first, at most MaxMergeRequestCommits.
369- Commits []domain.Commit
370- CommitsTruncated bool
371- // DiffBaseID is the commit the diff compares from; zero when the source
372- // branch is gone, then the diff is empty.
373- DiffBaseID domain.ObjectID
374- Stats domain.DiffStats
375- Diff domain.Diff
376- // Comments are the comments, oldest first.
377- Comments []domain.Comment
378- // LastReadID is the head recorded by MarkRead; zero when none.
379- LastReadID domain.ObjectID
380- // LastReadLost tells that since-last-read was asked for, a head was
381- // recorded, but that commit is gone: the diff is against the target.
382- LastReadLost bool
383-}
384-
385341 // Get returns a merge request with its commits, its diff, its comments and the
386342 // read position. The diff compares the merge base of the target and the head
387343 // with the head ("target...head"), within domain.DefaultDiffLimits. With
@@ -390,25 +346,25 @@ type MergeRequestDetail struct {
390346 // still exists; when none was recorded the diff is the default one, and when
391347 // the recorded commit is gone too, with LastReadLost set. A merged or closed
392348 // request keeps the head and the base it had.
393-func (m *MergeRequests) Get(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, sinceLastRead bool) (MergeRequestDetail, error) {
349+func (m *MergeRequests) Get(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, sinceLastRead bool) (ports.MergeRequestDetail, error) {
394350 _, mr, err := m.get(ctx, p, name, number)
395351 if err != nil {
396- return MergeRequestDetail{}, err
352+ return ports.MergeRequestDetail{}, err
397353 }
398354 if mr, err = m.withHead(ctx, mr); err != nil {
399- return MergeRequestDetail{}, err
355+ return ports.MergeRequestDetail{}, err
400356 }
401- d := MergeRequestDetail{MergeRequest: mr}
357+ d := ports.MergeRequestDetail{MergeRequest: mr}
402358 if d.Comments, err = m.store.ListComments(ctx, name, number); err != nil {
403- return MergeRequestDetail{}, fmt.Errorf("list comments of !%d in %q: %w", number, name, err)
359+ return ports.MergeRequestDetail{}, fmt.Errorf("list comments of !%d in %q: %w", number, name, err)
404360 }
405361 if d.LastReadID, err = m.store.GetRead(ctx, name, number); err != nil {
406- return MergeRequestDetail{}, err
362+ return ports.MergeRequestDetail{}, err
407363 }
408364
409365 exclude, base, err := m.rangeOf(ctx, mr)
410366 if err != nil {
411- return MergeRequestDetail{}, err
367+ return ports.MergeRequestDetail{}, err
412368 }
413369 head := mr.HeadID
414370 if head.IsZero() {
@@ -419,18 +375,18 @@ func (m *MergeRequests) Get(ctx context.Context, p domain.Principal, name domain
419375 // source branch; once it is deleted, gc may prune them.
420376 switch ok, err := m.git.HasCommit(ctx, name, head); {
421377 case err != nil:
422- return MergeRequestDetail{}, err
378+ return ports.MergeRequestDetail{}, err
423379 case !ok:
424380 return d, nil // the reviewed commits are gone: no commits, no diff
425381 }
426382 }
427383 if d.Commits, d.CommitsTruncated, err = m.git.ListRange(ctx, name, exclude, head, MaxMergeRequestCommits); err != nil {
428- return MergeRequestDetail{}, err
384+ return ports.MergeRequestDetail{}, err
429385 }
430386 if sinceLastRead && !d.LastReadID.IsZero() {
431387 switch ok, err := m.git.HasCommit(ctx, name, d.LastReadID); {
432388 case err != nil:
433- return MergeRequestDetail{}, err
389+ return ports.MergeRequestDetail{}, err
434390 case ok:
435391 base = d.LastReadID
436392 default:
@@ -442,7 +398,7 @@ func (m *MergeRequests) Get(ctx context.Context, p domain.Principal, name domain
442398 }
443399 d.DiffBaseID = base
444400 if d.Stats, d.Diff, err = m.git.DiffCommits(ctx, name, base, head, domain.DefaultDiffLimits()); err != nil {
445- return MergeRequestDetail{}, err
401+ return ports.MergeRequestDetail{}, err
446402 }
447403 return d, nil
448404 }
@@ -470,23 +426,13 @@ func (m *MergeRequests) rangeOf(ctx context.Context, mr domain.MergeRequest) (ex
470426 return tip, base, err
471427 }
472428
473-// AddCommentParams describes the comment to add.
474-type AddCommentParams struct {
475- // Path, Line and Side designate a line of the diff; all empty for a
476- // general comment.
477- Path string
478- Line int
479- Side domain.DiffSide
480- Text string
481-}
482-
483429 // AddComment adds a comment and publishes CommentAdded. The principal needs
484430 // mr:write. The text must follow the rules of ADR 0017 and not be blank; a
485431 // comment on a line must name a line of the current diff of the request (see
486432 // domain.Diff.HasLine), else the call fails with domain.ErrInvalid. A request
487433 // that is not open gives domain.ErrPreconditionFailed, and one that already
488434 // holds MaxCommentsPerMergeRequest comments domain.ErrResourceExhausted.
489-func (m *MergeRequests) AddComment(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params AddCommentParams) (domain.Comment, error) {
435+func (m *MergeRequests) AddComment(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params ports.AddCommentParams) (domain.Comment, error) {
490436 repo, err := m.writable(ctx, p, name, domain.ScopeMRWrite)
491437 if err != nil {
492438 return domain.Comment{}, err
@@ -567,7 +513,7 @@ func (m *MergeRequests) AddComment(ctx context.Context, p domain.Principal, name
567513
568514 // checkLine verifies that a comment designates a line of the current diff of
569515 // the request.
570-func (m *MergeRequests) checkLine(ctx context.Context, mr domain.MergeRequest, params AddCommentParams) error {
516+func (m *MergeRequests) checkLine(ctx context.Context, mr domain.MergeRequest, params ports.AddCommentParams) error {
571517 var v domain.Violations
572518 _, base, err := m.rangeOf(ctx, mr)
573519 if err != nil {
@@ -706,25 +652,6 @@ func (m *MergeRequests) MarkRead(ctx context.Context, p domain.Principal, name d
706652 return head, nil
707653 }
708654
709-// MergeParams describes the merge to perform.
710-type MergeParams struct {
711- // Mode defaults to fast-forward.
712- Mode domain.MergeMode
713- // HeadID is the commit the caller reviewed; the source branch must still
714- // point to it.
715- HeadID domain.ObjectID
716- // DeleteSource deletes the source branch after the merge.
717- DeleteSource bool
718-}
719-
720-// MergeResult is a merged request and what happened to its source branch.
721-type MergeResult struct {
722- domain.MergeRequest
723- // SourceDeleted is true when DeleteSource was asked and the branch is
724- // gone.
725- SourceDeleted bool
726-}
727-
728655 // Merge merges an open merge request, publishes MergeRequestMerged then a
729656 // PushEvent for the target branch, and returns the merged request. The
730657 // principal needs mr:merge. Merges of one repository run one at a time.
@@ -748,12 +675,12 @@ type MergeResult struct {
748675 // request. After the merge the source is deleted if it still points to the
749676 // head; the PushEvent then carries the deletion too. A failed deletion is
750677 // logged and leaves the branch: the merge stands and SourceDeleted is false.
751-func (m *MergeRequests) Merge(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params MergeParams) (MergeResult, error) {
678+func (m *MergeRequests) Merge(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params ports.MergeParams) (ports.MergeResult, error) {
752679 mr, deleted, err := m.merge(ctx, p, name, number, params)
753- return MergeResult{MergeRequest: mr, SourceDeleted: deleted}, err
680+ return ports.MergeResult{MergeRequest: mr, SourceDeleted: deleted}, err
754681 }
755682
756-func (m *MergeRequests) merge(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params MergeParams) (domain.MergeRequest, bool, error) {
683+func (m *MergeRequests) merge(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params ports.MergeParams) (domain.MergeRequest, bool, error) {
757684 repo, err := m.writable(ctx, p, name, domain.ScopeMRMerge)
758685 if err != nil {
759686 return domain.MergeRequest{}, false, err
M internal/app/mergerequests_delete_test.go +13 -12
@@ -12,6 +12,7 @@ import (
1212 "bnema.dev/ermite/internal/adapters/gitcli"
1313 "bnema.dev/ermite/internal/app"
1414 "bnema.dev/ermite/internal/domain"
15+ "bnema.dev/ermite/internal/ports"
1516 )
1617
1718 // A merge can delete its source branch; the push event carries both refs.
@@ -26,7 +27,7 @@ func TestMergeDeletesTheSourceBranch(t *testing.T) {
2627 mr := r.open("feat")
2728 r.drain()
2829
29- res, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{Mode: mode, HeadID: head, DeleteSource: true})
30+ res, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{Mode: mode, HeadID: head, DeleteSource: true})
3031 require.NoError(t, err)
3132 assert.True(t, res.SourceDeleted)
3233 assert.Equal(t, domain.MRStateMerged, res.State)
@@ -54,7 +55,7 @@ func TestMergeDeletesTheSourceBranch(t *testing.T) {
5455 r.seed()
5556 head := r.feature("feat")
5657 mr := r.open("feat")
57- res, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: head})
58+ res, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head})
5859 require.NoError(t, err)
5960 assert.False(t, res.SourceDeleted)
6061 assert.Equal(t, head, r.branchHead("feat"))
@@ -73,10 +74,10 @@ func TestMergeRefusesToDeleteANeededBranch(t *testing.T) {
7374 r.sh("checkout", "-q", "-B", "release", "main")
7475 r.push("release")
7576 mr := r.open("feat")
76- _, err := r.mrs.Create(r.ctx, root, r.repo, app.CreateMergeRequestParams{Title: "to release", Source: "feat", Target: "release"})
77+ _, err := r.mrs.Create(r.ctx, root, r.repo, ports.CreateMergeRequestParams{Title: "to release", Source: "feat", Target: "release"})
7778 require.NoError(t, err)
7879
79- _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: head, DeleteSource: true})
80+ _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head, DeleteSource: true})
8081 require.ErrorIs(t, err, domain.ErrPreconditionFailed)
8182 assert.Contains(t, err.Error(), "!2 still uses")
8283 assert.Equal(t, main, r.branchHead("main"), "not merged")
@@ -95,9 +96,9 @@ func TestMergeRefusesToDeleteANeededBranch(t *testing.T) {
9596 r.sh("checkout", "-q", "-B", "sub", "feat")
9697 r.commit(author, "d.txt", "dee\n", "add d")
9798 r.push("sub")
98- _, err := r.mrs.Create(r.ctx, root, r.repo, app.CreateMergeRequestParams{Title: "stacked", Source: "sub", Target: "feat"})
99+ _, err := r.mrs.Create(r.ctx, root, r.repo, ports.CreateMergeRequestParams{Title: "stacked", Source: "sub", Target: "feat"})
99100 require.NoError(t, err)
100- _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: head, DeleteSource: true})
101+ _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head, DeleteSource: true})
101102 require.ErrorIs(t, err, domain.ErrPreconditionFailed)
102103 assert.Equal(t, head, r.branchHead("feat"))
103104 })
@@ -109,9 +110,9 @@ func TestMergeRefusesToDeleteANeededBranch(t *testing.T) {
109110 r.sh("checkout", "-q", "-B", "old", "main~1")
110111 r.push("old")
111112 old := r.branchHead("old")
112- mr, err := r.mrs.Create(r.ctx, root, r.repo, app.CreateMergeRequestParams{Title: "back", Source: "main", Target: "old"})
113+ mr, err := r.mrs.Create(r.ctx, root, r.repo, ports.CreateMergeRequestParams{Title: "back", Source: "main", Target: "old"})
113114 require.NoError(t, err)
114- _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: main, DeleteSource: true})
115+ _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: main, DeleteSource: true})
115116 require.ErrorIs(t, err, domain.ErrPreconditionFailed)
116117 assert.Contains(t, err.Error(), "default branch")
117118 assert.Equal(t, old, r.branchHead("old"), "not merged")
@@ -127,7 +128,7 @@ func TestSquashMergedRequestReadsAfterItsCommitsArePruned(t *testing.T) {
127128 r.seed()
128129 head := r.feature("feat")
129130 mr := r.open("feat")
130- res, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{Mode: domain.MergeModeSquash, HeadID: head, DeleteSource: true})
131+ res, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{Mode: domain.MergeModeSquash, HeadID: head, DeleteSource: true})
131132 require.NoError(t, err)
132133 require.True(t, res.SourceDeleted)
133134
@@ -154,9 +155,9 @@ func TestCreateAfterAMergeDeletedTheBranch(t *testing.T) {
154155 r.seed()
155156 head := r.feature("feat")
156157 mr := r.open("feat")
157- _, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: head, DeleteSource: true})
158+ _, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head, DeleteSource: true})
158159 require.NoError(t, err)
159- _, err = r.mrs.Create(r.ctx, root, r.repo, app.CreateMergeRequestParams{Title: "late", Source: "feat"})
160+ _, err = r.mrs.Create(r.ctx, root, r.repo, ports.CreateMergeRequestParams{Title: "late", Source: "feat"})
160161 require.ErrorIs(t, err, domain.ErrInvalid)
161162 assert.Contains(t, fieldsOf(t, err), "source_branch")
162163 }
@@ -189,7 +190,7 @@ func TestMergeKeepsASourceThatMovedAfterTheMerge(t *testing.T) {
189190 })
190191 r.drain()
191192
192- res, err := mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: head, DeleteSource: true})
193+ res, err := mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head, DeleteSource: true})
193194 require.NoError(t, err, "the merge stands")
194195 assert.False(t, res.SourceDeleted)
195196 assert.Equal(t, domain.MRStateMerged, res.State)
M internal/app/mergerequests_integration_test.go +116 -115
@@ -23,8 +23,9 @@ import (
2323 "bnema.dev/ermite/internal/adapters/kvstore"
2424 "bnema.dev/ermite/internal/adapters/sshsig"
2525 "bnema.dev/ermite/internal/app"
26- appmocks "bnema.dev/ermite/internal/app/mocks"
2726 "bnema.dev/ermite/internal/domain"
27+ "bnema.dev/ermite/internal/ports"
28+ "bnema.dev/ermite/internal/ports/mocks"
2829 )
2930
3031 // queuedRepos records the repositories whose maintenance was queued.
@@ -58,7 +59,7 @@ type mrRig struct {
5859 store *kvstore.Store
5960 git *gitcli.Git
6061 bus *eventbus.Bus
61- sched *appmocks.MaintenanceScheduler
62+ sched *mocks.MaintenanceScheduler
6263 queued *queuedRepos
6364 events <-chan domain.Event
6465 work string
@@ -94,7 +95,7 @@ func newMRRig(t *testing.T) *mrRig {
9495 require.NoError(t, err)
9596 bus := eventbus.NewWithBuffer(1024)
9697 committer := domain.Identity{Name: "ermite", Email: "ermite@git.example.org"}
97- sched := appmocks.NewMaintenanceScheduler(t)
98+ sched := mocks.NewMaintenanceScheduler(t)
9899 queued := &queuedRepos{}
99100 sched.EXPECT().Schedule(mock.Anything, mock.Anything).Run(queued.add).Return().Maybe()
100101 r := &mrRig{
@@ -109,7 +110,7 @@ func newMRRig(t *testing.T) *mrRig {
109110 Meta: store, Store: store.MergeRequests(), Repos: git, Git: git, Signer: signer,
110111 Committer: committer, Bus: bus, Clock: fixedClock{}, Maint: r.sched,
111112 })
112- _, err = r.repos.Create(ctx, root, app.CreateParams{Name: r.repo, Visibility: domain.VisibilityPrivate})
113+ _, err = r.repos.Create(ctx, root, ports.CreateRepoParams{Name: r.repo, Visibility: domain.VisibilityPrivate})
113114 require.NoError(t, err)
114115 r.sh("init", "-q", "--initial-branch=main")
115116 return r
@@ -196,7 +197,7 @@ func (r *mrRig) feature(branch string) domain.ObjectID {
196197
197198 func (r *mrRig) open(branch string) domain.MergeRequest {
198199 r.t.Helper()
199- mr, err := r.mrs.Create(r.ctx, root, r.repo, app.CreateMergeRequestParams{Title: "Add " + branch, Source: branch})
200+ mr, err := r.mrs.Create(r.ctx, root, r.repo, ports.CreateMergeRequestParams{Title: "Add " + branch, Source: branch})
200201 require.NoError(r.t, err)
201202 return mr
202203 }
@@ -252,26 +253,26 @@ func TestCreateMergeRequestInputRules(t *testing.T) {
252253
253254 tests := []struct {
254255 name string
255- params app.CreateMergeRequestParams
256+ params ports.CreateMergeRequestParams
256257 field string // "" for valid
257258 }{
258- {"valid, default target", app.CreateMergeRequestParams{Title: "t", Source: "feat"}, ""},
259- {"valid, explicit target and description", app.CreateMergeRequestParams{Title: "t", Description: "a\nb", Source: "other", Target: "main"}, ""},
260- {"empty title", app.CreateMergeRequestParams{Title: "", Source: "feat", Target: "other"}, "title"},
261- {"two-line title", app.CreateMergeRequestParams{Title: "a\nb", Source: "feat", Target: "other"}, "title"},
262- {"title too long", app.CreateMergeRequestParams{Title: strings.Repeat("x", 121), Source: "feat", Target: "other"}, "title"},
263- {"title with a bidi override", app.CreateMergeRequestParams{Title: "a\u202eb", Source: "feat", Target: "other"}, "title"},
264- {"title with an escape", app.CreateMergeRequestParams{Title: "\x1b[31m", Source: "feat", Target: "other"}, "title"},
265- {"description with a control character", app.CreateMergeRequestParams{Title: "t", Description: "a\x07", Source: "feat", Target: "other"}, "description"},
266- {"description with a bidi isolate", app.CreateMergeRequestParams{Title: "t", Description: "a\u2066b", Source: "feat", Target: "other"}, "description"},
267- {"description too long", app.CreateMergeRequestParams{Title: "t", Description: strings.Repeat("x", 8001), Source: "feat", Target: "other"}, "description"},
268- {"description not utf-8", app.CreateMergeRequestParams{Title: "t", Description: "\xff", Source: "feat", Target: "other"}, "description"},
269- {"source missing", app.CreateMergeRequestParams{Title: "t", Source: ""}, "source_branch"},
270- {"source does not exist", app.CreateMergeRequestParams{Title: "t", Source: "nope", Target: "main"}, "source_branch"},
271- {"source is not a branch name", app.CreateMergeRequestParams{Title: "t", Source: "a..b", Target: "main"}, "source_branch"},
272- {"source is a full ref", app.CreateMergeRequestParams{Title: "t", Source: "refs/heads/feat", Target: "main"}, "source_branch"},
273- {"target does not exist", app.CreateMergeRequestParams{Title: "t", Source: "feat", Target: "nope"}, "target_branch"},
274- {"same branch", app.CreateMergeRequestParams{Title: "t", Source: "feat", Target: "feat"}, "target_branch"},
259+ {"valid, default target", ports.CreateMergeRequestParams{Title: "t", Source: "feat"}, ""},
260+ {"valid, explicit target and description", ports.CreateMergeRequestParams{Title: "t", Description: "a\nb", Source: "other", Target: "main"}, ""},
261+ {"empty title", ports.CreateMergeRequestParams{Title: "", Source: "feat", Target: "other"}, "title"},
262+ {"two-line title", ports.CreateMergeRequestParams{Title: "a\nb", Source: "feat", Target: "other"}, "title"},
263+ {"title too long", ports.CreateMergeRequestParams{Title: strings.Repeat("x", 121), Source: "feat", Target: "other"}, "title"},
264+ {"title with a bidi override", ports.CreateMergeRequestParams{Title: "a\u202eb", Source: "feat", Target: "other"}, "title"},
265+ {"title with an escape", ports.CreateMergeRequestParams{Title: "\x1b[31m", Source: "feat", Target: "other"}, "title"},
266+ {"description with a control character", ports.CreateMergeRequestParams{Title: "t", Description: "a\x07", Source: "feat", Target: "other"}, "description"},
267+ {"description with a bidi isolate", ports.CreateMergeRequestParams{Title: "t", Description: "a\u2066b", Source: "feat", Target: "other"}, "description"},
268+ {"description too long", ports.CreateMergeRequestParams{Title: "t", Description: strings.Repeat("x", 8001), Source: "feat", Target: "other"}, "description"},
269+ {"description not utf-8", ports.CreateMergeRequestParams{Title: "t", Description: "\xff", Source: "feat", Target: "other"}, "description"},
270+ {"source missing", ports.CreateMergeRequestParams{Title: "t", Source: ""}, "source_branch"},
271+ {"source does not exist", ports.CreateMergeRequestParams{Title: "t", Source: "nope", Target: "main"}, "source_branch"},
272+ {"source is not a branch name", ports.CreateMergeRequestParams{Title: "t", Source: "a..b", Target: "main"}, "source_branch"},
273+ {"source is a full ref", ports.CreateMergeRequestParams{Title: "t", Source: "refs/heads/feat", Target: "main"}, "source_branch"},
274+ {"target does not exist", ports.CreateMergeRequestParams{Title: "t", Source: "feat", Target: "nope"}, "target_branch"},
275+ {"same branch", ports.CreateMergeRequestParams{Title: "t", Source: "feat", Target: "feat"}, "target_branch"},
275276 }
276277 for _, tc := range tests {
277278 t.Run(tc.name, func(t *testing.T) {
@@ -290,7 +291,7 @@ func TestCreateMergeRequestInputRules(t *testing.T) {
290291 }
291292
292293 t.Run("a request is rejected whole with every field named", func(t *testing.T) {
293- _, err := r.mrs.Create(r.ctx, root, r.repo, app.CreateMergeRequestParams{Title: "a\nb", Description: "\x07", Source: "nope", Target: "feat"})
294+ _, err := r.mrs.Create(r.ctx, root, r.repo, ports.CreateMergeRequestParams{Title: "a\nb", Description: "\x07", Source: "nope", Target: "feat"})
294295 got := fieldsOf(t, err)
295296 assert.Contains(t, got, "title")
296297 assert.Contains(t, got, "description")
@@ -309,22 +310,22 @@ func TestCreateMergeRequestNumbersAndDuplicates(t *testing.T) {
309310 second := r.open("two")
310311 assert.Equal(t, int64(2), second.Number)
311312
312- _, err := r.mrs.Create(r.ctx, root, r.repo, app.CreateMergeRequestParams{Title: "again", Source: "one", Target: "main"})
313+ _, err := r.mrs.Create(r.ctx, root, r.repo, ports.CreateMergeRequestParams{Title: "again", Source: "one", Target: "main"})
313314 require.ErrorIs(t, err, domain.ErrAlreadyExists, "same open source and target")
314- _, err = r.mrs.Create(r.ctx, root, r.repo, app.CreateMergeRequestParams{Title: "again", Source: "one"})
315+ _, err = r.mrs.Create(r.ctx, root, r.repo, ports.CreateMergeRequestParams{Title: "again", Source: "one"})
315316 require.ErrorIs(t, err, domain.ErrAlreadyExists, "the default target is the same target")
316317
317318 // Another target is another request.
318319 r.sh("checkout", "-q", "-B", "stage", "main")
319320 r.push("stage")
320- third, err := r.mrs.Create(r.ctx, root, r.repo, app.CreateMergeRequestParams{Title: "to stage", Source: "one", Target: "stage"})
321+ third, err := r.mrs.Create(r.ctx, root, r.repo, ports.CreateMergeRequestParams{Title: "to stage", Source: "one", Target: "stage"})
321322 require.NoError(t, err)
322323 assert.Equal(t, int64(3), third.Number)
323324
324325 // Once the first is closed, the pair is free again, with a new number.
325326 _, err = r.mrs.Close(r.ctx, root, r.repo, first.Number)
326327 require.NoError(t, err)
327- fourth, err := r.mrs.Create(r.ctx, root, r.repo, app.CreateMergeRequestParams{Title: "retry", Source: "one"})
328+ fourth, err := r.mrs.Create(r.ctx, root, r.repo, ports.CreateMergeRequestParams{Title: "retry", Source: "one"})
328329 require.NoError(t, err)
329330 assert.Equal(t, int64(4), fourth.Number, "numbers are never reused")
330331 }
@@ -340,7 +341,7 @@ func TestMergeRequestAccess(t *testing.T) {
340341
341342 reads := map[string]func(p domain.Principal) error{
342343 "List": func(p domain.Principal) error {
343- _, err := r.mrs.List(r.ctx, p, r.repo, app.ListMergeRequestsParams{})
344+ _, err := r.mrs.List(r.ctx, p, r.repo, ports.ListMergeRequestsParams{})
344345 return err
345346 },
346347 "Get": func(p domain.Principal) error {
@@ -357,16 +358,16 @@ func TestMergeRequestAccess(t *testing.T) {
357358 }
358359 _, err := r.mrs.Get(r.ctx, reader, r.repo, n+1, false)
359360 assert.ErrorIs(t, err, domain.ErrNotFound, "unknown number")
360- _, err = r.mrs.List(r.ctx, root, name(t, "ghost"), app.ListMergeRequestsParams{})
361+ _, err = r.mrs.List(r.ctx, root, name(t, "ghost"), ports.ListMergeRequestsParams{})
361362 assert.ErrorIs(t, err, domain.ErrNotFound, "unknown repository")
362363
363364 writes := map[string]func(p domain.Principal) error{
364365 "Create": func(p domain.Principal) error {
365- _, err := r.mrs.Create(r.ctx, p, r.repo, app.CreateMergeRequestParams{Title: "t", Source: "feat", Target: "feat"})
366+ _, err := r.mrs.Create(r.ctx, p, r.repo, ports.CreateMergeRequestParams{Title: "t", Source: "feat", Target: "feat"})
366367 return err
367368 },
368369 "AddComment": func(p domain.Principal) error {
369- _, err := r.mrs.AddComment(r.ctx, p, r.repo, n, app.AddCommentParams{Text: "x"})
370+ _, err := r.mrs.AddComment(r.ctx, p, r.repo, n, ports.AddCommentParams{Text: "x"})
370371 return err
371372 },
372373 "ResolveComment": func(p domain.Principal) error {
@@ -382,7 +383,7 @@ func TestMergeRequestAccess(t *testing.T) {
382383 return err
383384 },
384385 "Merge": func(p domain.Principal) error {
385- _, err := r.mrs.Merge(r.ctx, p, r.repo, n+100, app.MergeParams{HeadID: mr.HeadID})
386+ _, err := r.mrs.Merge(r.ctx, p, r.repo, n+100, ports.MergeParams{HeadID: mr.HeadID})
386387 return err
387388 },
388389 }
@@ -413,11 +414,11 @@ func TestMergeRequestAccess(t *testing.T) {
413414 // On a public repository an anonymous caller reads, but writing is
414415 // unauthenticated rather than not found.
415416 pubName := name(t, "pub")
416- _, err = r.repos.Create(r.ctx, root, app.CreateParams{Name: pubName, Visibility: domain.VisibilityPublic})
417+ _, err = r.repos.Create(r.ctx, root, ports.CreateRepoParams{Name: pubName, Visibility: domain.VisibilityPublic})
417418 require.NoError(t, err)
418- _, err = r.mrs.List(r.ctx, domain.Anonymous(), pubName, app.ListMergeRequestsParams{})
419+ _, err = r.mrs.List(r.ctx, domain.Anonymous(), pubName, ports.ListMergeRequestsParams{})
419420 assert.NoError(t, err)
420- _, err = r.mrs.Create(r.ctx, domain.Anonymous(), pubName, app.CreateMergeRequestParams{Title: "t", Source: "a", Target: "b"})
421+ _, err = r.mrs.Create(r.ctx, domain.Anonymous(), pubName, ports.CreateMergeRequestParams{Title: "t", Source: "a", Target: "b"})
421422 assert.ErrorIs(t, err, domain.ErrUnauthenticated)
422423 }
423424
@@ -434,24 +435,24 @@ func TestListMergeRequests(t *testing.T) {
434435 require.NoError(t, err)
435436 r.sh("checkout", "-q", "main")
436437 merged := r.feature("f0")
437- _, err = r.mrs.Merge(r.ctx, root, r.repo, 1, app.MergeParams{HeadID: merged})
438+ _, err = r.mrs.Merge(r.ctx, root, r.repo, 1, ports.MergeParams{HeadID: merged})
438439 require.NoError(t, err)
439440 r.sh("branch", "-D", "f4")
440441 r.run(r.bare(), r.env(author), "branch", "-D", "f4")
441442
442- numbers := func(p app.MergeRequestPage) []int64 {
443+ numbers := func(p ports.MergeRequestPage) []int64 {
443444 var out []int64
444445 for _, m := range p.MergeRequests {
445446 out = append(out, m.Number)
446447 }
447448 return out
448449 }
449- all, err := r.mrs.List(r.ctx, root, r.repo, app.ListMergeRequestsParams{})
450+ all, err := r.mrs.List(r.ctx, root, r.repo, ports.ListMergeRequestsParams{})
450451 require.NoError(t, err)
451452 assert.Equal(t, []int64{5, 4, 3, 2, 1}, numbers(all), "highest number first")
452453 assert.Empty(t, all.NextPageToken)
453454
454- open, err := r.mrs.List(r.ctx, root, r.repo, app.ListMergeRequestsParams{State: domain.MRStateOpen})
455+ open, err := r.mrs.List(r.ctx, root, r.repo, ports.ListMergeRequestsParams{State: domain.MRStateOpen})
455456 require.NoError(t, err)
456457 assert.Equal(t, []int64{5, 4, 3}, numbers(open))
457458 for _, m := range open.MergeRequests {
@@ -461,35 +462,35 @@ func TestListMergeRequests(t *testing.T) {
461462 assert.False(t, m.HeadID.IsZero(), "an open request carries its head")
462463 }
463464 }
464- closed, err := r.mrs.List(r.ctx, root, r.repo, app.ListMergeRequestsParams{State: domain.MRStateClosed})
465+ closed, err := r.mrs.List(r.ctx, root, r.repo, ports.ListMergeRequestsParams{State: domain.MRStateClosed})
465466 require.NoError(t, err)
466467 assert.Equal(t, []int64{2}, numbers(closed))
467- mergedPage, err := r.mrs.List(r.ctx, root, r.repo, app.ListMergeRequestsParams{State: domain.MRStateMerged})
468+ mergedPage, err := r.mrs.List(r.ctx, root, r.repo, ports.ListMergeRequestsParams{State: domain.MRStateMerged})
468469 require.NoError(t, err)
469470 assert.Equal(t, []int64{1}, numbers(mergedPage))
470471 assert.Equal(t, merged, mergedPage.MergeRequests[0].HeadID, "a merged request keeps the head it had")
471472
472473 // Paging: the token names the last number.
473- p1, err := r.mrs.List(r.ctx, root, r.repo, app.ListMergeRequestsParams{PageSize: 2})
474+ p1, err := r.mrs.List(r.ctx, root, r.repo, ports.ListMergeRequestsParams{PageSize: 2})
474475 require.NoError(t, err)
475476 assert.Equal(t, []int64{5, 4}, numbers(p1))
476477 require.NotEmpty(t, p1.NextPageToken)
477478 // A request created between the pages does not shift them.
478479 r.feature("late")
479480 r.open("late")
480- p2, err := r.mrs.List(r.ctx, root, r.repo, app.ListMergeRequestsParams{PageSize: 2, PageToken: p1.NextPageToken})
481+ p2, err := r.mrs.List(r.ctx, root, r.repo, ports.ListMergeRequestsParams{PageSize: 2, PageToken: p1.NextPageToken})
481482 require.NoError(t, err)
482483 assert.Equal(t, []int64{3, 2}, numbers(p2))
483- p3, err := r.mrs.List(r.ctx, root, r.repo, app.ListMergeRequestsParams{PageSize: 2, PageToken: p2.NextPageToken})
484+ p3, err := r.mrs.List(r.ctx, root, r.repo, ports.ListMergeRequestsParams{PageSize: 2, PageToken: p2.NextPageToken})
484485 require.NoError(t, err)
485486 assert.Equal(t, []int64{1}, numbers(p3))
486487 assert.Empty(t, p3.NextPageToken)
487488
488489 for _, tok := range []string{"garbage", "bW9yZQ"} {
489- _, err = r.mrs.List(r.ctx, root, r.repo, app.ListMergeRequestsParams{PageToken: tok})
490+ _, err = r.mrs.List(r.ctx, root, r.repo, ports.ListMergeRequestsParams{PageToken: tok})
490491 assert.ErrorIs(t, err, domain.ErrInvalidPageToken)
491492 }
492- _, err = r.mrs.List(r.ctx, root, r.repo, app.ListMergeRequestsParams{PageSize: -1})
493+ _, err = r.mrs.List(r.ctx, root, r.repo, ports.ListMergeRequestsParams{PageSize: -1})
493494 assert.ErrorIs(t, err, domain.ErrInvalidPageSize)
494495 _ = nums
495496 }
@@ -499,9 +500,9 @@ func TestGetMergeRequest(t *testing.T) {
499500 main := r.seed()
500501 head := r.feature("feat")
501502 mr := r.open("feat")
502- _, err := r.mrs.AddComment(r.ctx, root, r.repo, mr.Number, app.AddCommentParams{Text: "first"})
503+ _, err := r.mrs.AddComment(r.ctx, root, r.repo, mr.Number, ports.AddCommentParams{Text: "first"})
503504 require.NoError(t, err)
504- _, err = r.mrs.AddComment(r.ctx, root, r.repo, mr.Number, app.AddCommentParams{Text: "second"})
505+ _, err = r.mrs.AddComment(r.ctx, root, r.repo, mr.Number, ports.AddCommentParams{Text: "second"})
505506 require.NoError(t, err)
506507
507508 d, err := r.mrs.Get(r.ctx, reader, r.repo, mr.Number, false)
@@ -565,30 +566,30 @@ func TestAddComment(t *testing.T) {
565566
566567 tests := []struct {
567568 name string
568- params app.AddCommentParams
569+ params ports.AddCommentParams
569570 field string // "" for valid
570571 }{
571- {"general", app.AddCommentParams{Text: "looks good"}, ""},
572- {"multi-line general", app.AddCommentParams{Text: "a\n\nb"}, ""},
573- {"new side of a changed line", app.AddCommentParams{Path: "a.txt", Line: 2, Side: domain.SideNew, Text: "x"}, ""},
574- {"old side of a changed line", app.AddCommentParams{Path: "a.txt", Line: 2, Side: domain.SideOld, Text: "x"}, ""},
575- {"context line", app.AddCommentParams{Path: "a.txt", Line: 3, Side: domain.SideNew, Text: "x"}, ""},
576- {"added file", app.AddCommentParams{Path: "c.txt", Line: 1, Side: domain.SideNew, Text: "x"}, ""},
577- {"empty text", app.AddCommentParams{Text: ""}, "text"},
578- {"blank text", app.AddCommentParams{Text: " \n "}, "text"},
579- {"text too long", app.AddCommentParams{Text: strings.Repeat("x", 8001)}, "text"},
580- {"text with a control character", app.AddCommentParams{Text: "a\x1b[0m"}, "text"},
581- {"text with a tab", app.AddCommentParams{Text: "a\tb"}, "text"},
582- {"text with a bidi override", app.AddCommentParams{Text: "a\u202eb"}, "text"},
583- {"line outside the diff", app.AddCommentParams{Path: "a.txt", Line: 40, Side: domain.SideNew, Text: "x"}, "path"},
584- {"unchanged file", app.AddCommentParams{Path: "b.txt", Line: 1, Side: domain.SideNew, Text: "x"}, "path"},
585- {"unknown path", app.AddCommentParams{Path: "nope.txt", Line: 1, Side: domain.SideNew, Text: "x"}, "path"},
586- {"deleted line on the wrong side", app.AddCommentParams{Path: "c.txt", Line: 1, Side: domain.SideOld, Text: "x"}, "path"},
587- {"path without a line", app.AddCommentParams{Path: "a.txt", Side: domain.SideNew, Text: "x"}, "line"},
588- {"path without a side", app.AddCommentParams{Path: "a.txt", Line: 2, Text: "x"}, "side"},
589- {"line without a path", app.AddCommentParams{Line: 2, Side: domain.SideNew, Text: "x"}, "path"},
590- {"side without a path", app.AddCommentParams{Side: domain.SideOld, Text: "x"}, "path"},
591- {"negative line", app.AddCommentParams{Path: "a.txt", Line: -2, Side: domain.SideNew, Text: "x"}, "line"},
572+ {"general", ports.AddCommentParams{Text: "looks good"}, ""},
573+ {"multi-line general", ports.AddCommentParams{Text: "a\n\nb"}, ""},
574+ {"new side of a changed line", ports.AddCommentParams{Path: "a.txt", Line: 2, Side: domain.SideNew, Text: "x"}, ""},
575+ {"old side of a changed line", ports.AddCommentParams{Path: "a.txt", Line: 2, Side: domain.SideOld, Text: "x"}, ""},
576+ {"context line", ports.AddCommentParams{Path: "a.txt", Line: 3, Side: domain.SideNew, Text: "x"}, ""},
577+ {"added file", ports.AddCommentParams{Path: "c.txt", Line: 1, Side: domain.SideNew, Text: "x"}, ""},
578+ {"empty text", ports.AddCommentParams{Text: ""}, "text"},
579+ {"blank text", ports.AddCommentParams{Text: " \n "}, "text"},
580+ {"text too long", ports.AddCommentParams{Text: strings.Repeat("x", 8001)}, "text"},
581+ {"text with a control character", ports.AddCommentParams{Text: "a\x1b[0m"}, "text"},
582+ {"text with a tab", ports.AddCommentParams{Text: "a\tb"}, "text"},
583+ {"text with a bidi override", ports.AddCommentParams{Text: "a\u202eb"}, "text"},
584+ {"line outside the diff", ports.AddCommentParams{Path: "a.txt", Line: 40, Side: domain.SideNew, Text: "x"}, "path"},
585+ {"unchanged file", ports.AddCommentParams{Path: "b.txt", Line: 1, Side: domain.SideNew, Text: "x"}, "path"},
586+ {"unknown path", ports.AddCommentParams{Path: "nope.txt", Line: 1, Side: domain.SideNew, Text: "x"}, "path"},
587+ {"deleted line on the wrong side", ports.AddCommentParams{Path: "c.txt", Line: 1, Side: domain.SideOld, Text: "x"}, "path"},
588+ {"path without a line", ports.AddCommentParams{Path: "a.txt", Side: domain.SideNew, Text: "x"}, "line"},
589+ {"path without a side", ports.AddCommentParams{Path: "a.txt", Line: 2, Text: "x"}, "side"},
590+ {"line without a path", ports.AddCommentParams{Line: 2, Side: domain.SideNew, Text: "x"}, "path"},
591+ {"side without a path", ports.AddCommentParams{Side: domain.SideOld, Text: "x"}, "path"},
592+ {"negative line", ports.AddCommentParams{Path: "a.txt", Line: -2, Side: domain.SideNew, Text: "x"}, "line"},
592593 }
593594 for _, tc := range tests {
594595 t.Run(tc.name, func(t *testing.T) {
@@ -625,7 +626,7 @@ func TestAddComment(t *testing.T) {
625626 t.Run("not open", func(t *testing.T) {
626627 _, err := r.mrs.Close(r.ctx, mrW, r.repo, n)
627628 require.NoError(t, err)
628- _, err = r.mrs.AddComment(r.ctx, mrW, r.repo, n, app.AddCommentParams{Text: "late"})
629+ _, err = r.mrs.AddComment(r.ctx, mrW, r.repo, n, ports.AddCommentParams{Text: "late"})
629630 assert.ErrorIs(t, err, domain.ErrPreconditionFailed)
630631 _, err = r.mrs.ResolveComment(r.ctx, mrW, r.repo, n, 1, true)
631632 assert.ErrorIs(t, err, domain.ErrPreconditionFailed)
@@ -642,7 +643,7 @@ func TestConcurrentComments(t *testing.T) {
642643 ids := make(chan int64, workers)
643644 for i := range workers {
644645 wg.Go(func() {
645- c, err := r.mrs.AddComment(r.ctx, mrW, r.repo, mr.Number, app.AddCommentParams{Text: fmt.Sprintf("c%d", i)})
646+ c, err := r.mrs.AddComment(r.ctx, mrW, r.repo, mr.Number, ports.AddCommentParams{Text: fmt.Sprintf("c%d", i)})
646647 if assert.NoError(t, err) {
647648 ids <- c.ID
648649 }
@@ -666,7 +667,7 @@ func TestResolveComment(t *testing.T) {
666667 r.seed()
667668 r.feature("feat")
668669 mr := r.open("feat")
669- c, err := r.mrs.AddComment(r.ctx, mrW, r.repo, mr.Number, app.AddCommentParams{Text: "fix this"})
670+ c, err := r.mrs.AddComment(r.ctx, mrW, r.repo, mr.Number, ports.AddCommentParams{Text: "fix this"})
670671 require.NoError(t, err)
671672 r.drain()
672673
@@ -698,9 +699,9 @@ func TestEventsOfAMergeRequest(t *testing.T) {
698699 r.drain()
699700
700701 mr := r.open("feat")
701- c, err := r.mrs.AddComment(r.ctx, mrW, r.repo, mr.Number, app.AddCommentParams{Text: "note"})
702+ c, err := r.mrs.AddComment(r.ctx, mrW, r.repo, mr.Number, ports.AddCommentParams{Text: "note"})
702703 require.NoError(t, err)
703- _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: head})
704+ _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head})
704705 require.NoError(t, err)
705706
706707 r.feature("other")
@@ -732,7 +733,7 @@ func TestEventsOfAMergeRequest(t *testing.T) {
732733 // Failed calls publish nothing.
733734 _, err = r.mrs.Close(r.ctx, mrW, r.repo, other.Number)
734735 require.Error(t, err)
735- _, err = r.mrs.AddComment(r.ctx, mrW, r.repo, other.Number, app.AddCommentParams{Text: "x"})
736+ _, err = r.mrs.AddComment(r.ctx, mrW, r.repo, other.Number, ports.AddCommentParams{Text: "x"})
736737 require.Error(t, err)
737738 assert.Empty(t, r.drain())
738739 }
@@ -859,7 +860,7 @@ func TestMergeFastForward(t *testing.T) {
859860 mr := r.open("feat")
860861 r.drain()
861862
862- merged, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: head})
863+ merged, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head})
863864 require.NoError(t, err)
864865 assert.Equal(t, domain.MRStateMerged, merged.State)
865866 assert.Equal(t, domain.MergeModeFastForward, merged.MergeMode, "the default mode")
@@ -880,11 +881,11 @@ func TestMergeFastForward(t *testing.T) {
880881 assert.Len(t, d.Diff.Files, 2)
881882
882883 // Repeating it, or touching a final request, is a failed precondition.
883- _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: head})
884+ _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head})
884885 assert.ErrorIs(t, err, domain.ErrPreconditionFailed)
885886 _, err = r.mrs.Close(r.ctx, mrW, r.repo, mr.Number)
886887 assert.ErrorIs(t, err, domain.ErrPreconditionFailed)
887- _, err = r.mrs.AddComment(r.ctx, mrW, r.repo, mr.Number, app.AddCommentParams{Text: "x"})
888+ _, err = r.mrs.AddComment(r.ctx, mrW, r.repo, mr.Number, ports.AddCommentParams{Text: "x"})
888889 assert.ErrorIs(t, err, domain.ErrPreconditionFailed)
889890 }
890891
@@ -897,14 +898,14 @@ func TestMergeRefusals(t *testing.T) {
897898 mr := r.open("feat")
898899 r.commit(author, "more.txt", "m\n", "pushed after the review")
899900 r.push("feat")
900- _, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: head})
901+ _, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head})
901902 require.ErrorIs(t, err, domain.ErrPreconditionFailed)
902903 assert.Contains(t, err.Error(), "branch moved, read again")
903904 assert.NotEqual(t, head, r.branchHead("main"), "main did not move")
904905 // Reading again gives the new head, which merges.
905906 d, err := r.mrs.Get(r.ctx, mrM, r.repo, mr.Number, false)
906907 require.NoError(t, err)
907- _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: d.MergeRequest.HeadID})
908+ _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: d.MergeRequest.HeadID})
908909 require.NoError(t, err)
909910 })
910911
@@ -919,7 +920,7 @@ func TestMergeRefusals(t *testing.T) {
919920 tip := r.branchHead("main")
920921
921922 for _, mode := range []domain.MergeMode{domain.MergeModeFastForward, domain.MergeModeSquash} {
922- _, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: head, Mode: mode})
923+ _, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head, Mode: mode})
923924 require.ErrorIs(t, err, domain.ErrPreconditionFailed, mode)
924925 assert.Contains(t, err.Error(), "rebase", mode)
925926 }
@@ -932,7 +933,7 @@ func TestMergeRefusals(t *testing.T) {
932933 r.sh("checkout", "-q", "feat")
933934 r.sh("rebase", "-q", "main")
934935 r.push("feat")
935- _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: r.head()})
936+ _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: r.head()})
936937 require.NoError(t, err)
937938 })
938939
@@ -942,13 +943,13 @@ func TestMergeRefusals(t *testing.T) {
942943 head := r.feature("feat")
943944 mr := r.open("feat")
944945 modes := []domain.MergeMode{domain.MergeModeFastForward}
945- _, err := r.repos.Update(r.ctx, root, r.repo, app.UpdateParams{AllowedMergeModes: &modes})
946+ _, err := r.repos.Update(r.ctx, root, r.repo, ports.UpdateRepoParams{AllowedMergeModes: &modes})
946947 require.NoError(t, err)
947948
948- _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: head, Mode: domain.MergeModeSquash})
949+ _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head, Mode: domain.MergeModeSquash})
949950 require.ErrorIs(t, err, domain.ErrPreconditionFailed)
950951 assert.Contains(t, err.Error(), "squash")
951- _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: head})
952+ _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head})
952953 require.NoError(t, err, "fast-forward is allowed")
953954 })
954955
@@ -958,11 +959,11 @@ func TestMergeRefusals(t *testing.T) {
958959 head := r.feature("feat")
959960 mr := r.open("feat")
960961 modes := []domain.MergeMode{domain.MergeModeSquash}
961- _, err := r.repos.Update(r.ctx, root, r.repo, app.UpdateParams{AllowedMergeModes: &modes})
962+ _, err := r.repos.Update(r.ctx, root, r.repo, ports.UpdateRepoParams{AllowedMergeModes: &modes})
962963 require.NoError(t, err)
963- _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: head})
964+ _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head})
964965 require.ErrorIs(t, err, domain.ErrPreconditionFailed)
965- _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: head, Mode: domain.MergeModeSquash})
966+ _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head, Mode: domain.MergeModeSquash})
966967 require.NoError(t, err)
967968 })
968969
@@ -971,13 +972,13 @@ func TestMergeRefusals(t *testing.T) {
971972 r.seed()
972973 head := r.feature("feat")
973974 mr := r.open("feat")
974- _, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{})
975+ _, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{})
975976 require.ErrorIs(t, err, domain.ErrInvalid)
976977 assert.Contains(t, fieldsOf(t, err), "head_id")
977- _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: head, Mode: "rebase"})
978+ _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head, Mode: "rebase"})
978979 require.ErrorIs(t, err, domain.ErrInvalid)
979980 assert.Contains(t, fieldsOf(t, err), "mode")
980- _, err = r.mrs.Merge(r.ctx, mrM, r.repo, 99, app.MergeParams{HeadID: head})
981+ _, err = r.mrs.Merge(r.ctx, mrM, r.repo, 99, ports.MergeParams{HeadID: head})
981982 assert.ErrorIs(t, err, domain.ErrNotFound)
982983 })
983984
@@ -987,13 +988,13 @@ func TestMergeRefusals(t *testing.T) {
987988 head := r.feature("feat")
988989 mr := r.open("feat")
989990 r.run(r.bare(), r.env(author), "branch", "-D", "feat")
990- _, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: head})
991+ _, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head})
991992 require.ErrorIs(t, err, domain.ErrPreconditionFailed)
992993 assert.Contains(t, err.Error(), "no longer exists")
993994
994995 r.push("feat")
995996 r.run(r.bare(), r.env(author), "update-ref", "refs/heads/main", head.String())
996- _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: head})
997+ _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head})
997998 require.ErrorIs(t, err, domain.ErrPreconditionFailed, "the target already is at the head")
998999 assert.Contains(t, err.Error(), "nothing to merge")
9991000 })
@@ -1022,7 +1023,7 @@ func TestConcurrentMergesSerialize(t *testing.T) {
10221023 continue
10231024 }
10241025 wg.Go(func() {
1025- _, err := r.mrs.Merge(r.ctx, mrM, r.repo, number, app.MergeParams{HeadID: head, Mode: mode})
1026+ _, err := r.mrs.Merge(r.ctx, mrM, r.repo, number, ports.MergeParams{HeadID: head, Mode: mode})
10261027 results <- outcome{number, err}
10271028 })
10281029 }
@@ -1079,11 +1080,11 @@ func TestSquashMerge(t *testing.T) {
10791080 r.commit(bob, "w.txt", "w\n", "bob again")
10801081 r.push("feat")
10811082 head := r.branchHead("feat")
1082- mr, err := r.mrs.Create(r.ctx, root, r.repo, app.CreateMergeRequestParams{Title: "Add things", Source: "feat"})
1083+ mr, err := r.mrs.Create(r.ctx, root, r.repo, ports.CreateMergeRequestParams{Title: "Add things", Source: "feat"})
10831084 require.NoError(t, err)
10841085 r.drain()
10851086
1086- merged, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: head, Mode: domain.MergeModeSquash})
1087+ merged, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: head, Mode: domain.MergeModeSquash})
10871088 require.NoError(t, err)
10881089 assert.Equal(t, domain.MergeModeSquash, merged.MergeMode)
10891090 require.False(t, merged.MergedCommitID.IsZero())
@@ -1144,7 +1145,7 @@ func TestSquashSingleCommitHasNoTrailer(t *testing.T) {
11441145 r.commit(author, "m.txt", "m\n", "only")
11451146 r.push("mine")
11461147 mr := r.open("mine")
1147- merged, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{HeadID: r.branchHead("mine"), Mode: domain.MergeModeSquash})
1148+ merged, err := r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{HeadID: r.branchHead("mine"), Mode: domain.MergeModeSquash})
11481149 require.NoError(t, err)
11491150 msg := r.run(r.bare(), r.env(author), "log", "-1", "--format=%B", merged.MergedCommitID.String())
11501151 assert.Equal(t, "Add mine (!1)\n\n- only", msg)
@@ -1159,7 +1160,7 @@ func TestUpdateRepoSettings(t *testing.T) {
11591160
11601161 modes := []domain.MergeMode{domain.MergeModeSquash, domain.MergeModeSquash}
11611162 off := false
1162- got, err := r.repos.Update(r.ctx, root, r.repo, app.UpdateParams{AllowedMergeModes: &modes, FailingChecksBlock: &off})
1163+ got, err := r.repos.Update(r.ctx, root, r.repo, ports.UpdateRepoParams{AllowedMergeModes: &modes, FailingChecksBlock: &off})
11631164 require.NoError(t, err)
11641165 assert.Equal(t, domain.RepoSettings{AllowedMergeModes: []domain.MergeMode{domain.MergeModeSquash}, FailingChecksBlock: false}, got.Settings)
11651166 again, err := r.repos.Get(r.ctx, root, r.repo)
@@ -1168,14 +1169,14 @@ func TestUpdateRepoSettings(t *testing.T) {
11681169
11691170 // Only the named setting changes.
11701171 on := true
1171- got, err = r.repos.Update(r.ctx, root, r.repo, app.UpdateParams{FailingChecksBlock: &on})
1172+ got, err = r.repos.Update(r.ctx, root, r.repo, ports.UpdateRepoParams{FailingChecksBlock: &on})
11721173 require.NoError(t, err)
11731174 assert.Equal(t, []domain.MergeMode{domain.MergeModeSquash}, got.Settings.AllowedMergeModes)
11741175 assert.True(t, got.Settings.FailingChecksBlock)
11751176
11761177 for _, bad := range [][]domain.MergeMode{{}, nil, {"rebase"}} {
11771178 bad := bad
1178- _, err = r.repos.Update(r.ctx, root, r.repo, app.UpdateParams{AllowedMergeModes: &bad})
1179+ _, err = r.repos.Update(r.ctx, root, r.repo, ports.UpdateRepoParams{AllowedMergeModes: &bad})
11791180 assert.ErrorIs(t, err, domain.ErrInvalid, "%v", bad)
11801181 }
11811182 again, err = r.repos.Get(r.ctx, root, r.repo)
@@ -1183,7 +1184,7 @@ func TestUpdateRepoSettings(t *testing.T) {
11831184 assert.Equal(t, got.Settings, again.Settings, "a refused change stores nothing")
11841185
11851186 // repo:admin is needed, like for any other field.
1186- _, err = r.repos.Update(r.ctx, writer(domain.ScopeRepoRead, domain.ScopeMRMerge), r.repo, app.UpdateParams{FailingChecksBlock: &off})
1187+ _, err = r.repos.Update(r.ctx, writer(domain.ScopeRepoRead, domain.ScopeMRMerge), r.repo, ports.UpdateRepoParams{FailingChecksBlock: &off})
11871188 assert.ErrorIs(t, err, domain.ErrForbidden)
11881189 }
11891190
@@ -1194,13 +1195,13 @@ func TestDeletingARepositoryDropsItsMergeRequests(t *testing.T) {
11941195 r.open("feat")
11951196 require.NoError(t, r.repos.Delete(r.ctx, root, r.repo))
11961197
1197- _, err := r.mrs.List(r.ctx, root, r.repo, app.ListMergeRequestsParams{})
1198+ _, err := r.mrs.List(r.ctx, root, r.repo, ports.ListMergeRequestsParams{})
11981199 assert.ErrorIs(t, err, domain.ErrNotFound)
11991200
12001201 // Created again under the same name, it starts empty.
1201- _, err = r.repos.Create(r.ctx, root, app.CreateParams{Name: r.repo})
1202+ _, err = r.repos.Create(r.ctx, root, ports.CreateRepoParams{Name: r.repo})
12021203 require.NoError(t, err)
1203- page, err := r.mrs.List(r.ctx, root, r.repo, app.ListMergeRequestsParams{})
1204+ page, err := r.mrs.List(r.ctx, root, r.repo, ports.ListMergeRequestsParams{})
12041205 require.NoError(t, err)
12051206 assert.Empty(t, page.MergeRequests)
12061207 }
@@ -1222,12 +1223,12 @@ func TestAgentPresetWorksOnItsPrivateRepository(t *testing.T) {
12221223 r.seed()
12231224 head := r.feature("feat")
12241225
1225- mr, err := r.mrs.Create(r.ctx, agent, r.repo, app.CreateMergeRequestParams{Title: "From the agent", Source: "feat"})
1226+ mr, err := r.mrs.Create(r.ctx, agent, r.repo, ports.CreateMergeRequestParams{Title: "From the agent", Source: "feat"})
12261227 require.NoError(t, err)
12271228 assert.Equal(t, head, mr.HeadID)
12281229 assert.Equal(t, "agent-1", mr.Author)
12291230
1230- c, err := r.mrs.AddComment(r.ctx, agent, r.repo, mr.Number, app.AddCommentParams{Text: "note"})
1231+ c, err := r.mrs.AddComment(r.ctx, agent, r.repo, mr.Number, ports.AddCommentParams{Text: "note"})
12311232 require.NoError(t, err)
12321233 _, err = r.mrs.ResolveComment(r.ctx, agent, r.repo, mr.Number, c.ID, true)
12331234 require.NoError(t, err)
@@ -1235,7 +1236,7 @@ func TestAgentPresetWorksOnItsPrivateRepository(t *testing.T) {
12351236 require.NoError(t, err)
12361237
12371238 // repo:write alone does not read merge requests.
1238- _, err = r.mrs.List(r.ctx, agent, r.repo, app.ListMergeRequestsParams{})
1239+ _, err = r.mrs.List(r.ctx, agent, r.repo, ports.ListMergeRequestsParams{})
12391240 assert.ErrorIs(t, err, domain.ErrNotFound)
12401241 _, err = r.mrs.Get(r.ctx, agent, r.repo, mr.Number, false)
12411242 assert.ErrorIs(t, err, domain.ErrNotFound)
@@ -1245,11 +1246,11 @@ func TestAgentPresetWorksOnItsPrivateRepository(t *testing.T) {
12451246 assert.Equal(t, domain.MRStateClosed, closed.State)
12461247
12471248 // An unknown repository is not found whatever the scopes.
1248- _, err = r.mrs.Create(r.ctx, agent, name(t, "ghost"), app.CreateMergeRequestParams{Title: "t", Source: "feat"})
1249+ _, err = r.mrs.Create(r.ctx, agent, name(t, "ghost"), ports.CreateMergeRequestParams{Title: "t", Source: "feat"})
12491250 assert.ErrorIs(t, err, domain.ErrNotFound)
12501251 // A token limited to other repositories does not get the scope here.
12511252 elsewhere := domain.TokenPrincipal(domain.Token{ID: "e", Scopes: []domain.Scope{domain.ScopeRepoWrite, domain.ScopeMRWrite}, Repos: []domain.RepoName{name(t, "else")}})
1252- _, err = r.mrs.Create(r.ctx, elsewhere, r.repo, app.CreateMergeRequestParams{Title: "t", Source: "feat"})
1253+ _, err = r.mrs.Create(r.ctx, elsewhere, r.repo, ports.CreateMergeRequestParams{Title: "t", Source: "feat"})
12531254 assert.ErrorIs(t, err, domain.ErrNotFound)
12541255 }
12551256
@@ -1267,7 +1268,7 @@ func TestMergeRecordsAPush(t *testing.T) {
12671268 require.True(t, before.PushedAt.IsZero(), "no push recorded yet")
12681269 assert.Empty(t, r.queued.names())
12691270
1270- _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, app.MergeParams{Mode: mode, HeadID: head})
1271+ _, err = r.mrs.Merge(r.ctx, mrM, r.repo, mr.Number, ports.MergeParams{Mode: mode, HeadID: head})
12711272 require.NoError(t, err)
12721273
12731274 after, err := r.repos.Get(r.ctx, root, r.repo)
@@ -1285,10 +1286,10 @@ func TestCommentsPerMergeRequestAreBounded(t *testing.T) {
12851286 r.feature("feat")
12861287 mr := r.open("feat")
12871288 for i := range app.MaxCommentsPerMergeRequest {
1288- _, err := r.mrs.AddComment(r.ctx, mrW, r.repo, mr.Number, app.AddCommentParams{Text: fmt.Sprintf("c%d", i)})
1289+ _, err := r.mrs.AddComment(r.ctx, mrW, r.repo, mr.Number, ports.AddCommentParams{Text: fmt.Sprintf("c%d", i)})
12891290 require.NoError(t, err, "comment %d", i+1)
12901291 }
1291- _, err := r.mrs.AddComment(r.ctx, mrW, r.repo, mr.Number, app.AddCommentParams{Text: "one too many"})
1292+ _, err := r.mrs.AddComment(r.ctx, mrW, r.repo, mr.Number, ports.AddCommentParams{Text: "one too many"})
12921293 assert.ErrorIs(t, err, domain.ErrResourceExhausted)
12931294 d, err := r.mrs.Get(r.ctx, root, r.repo, mr.Number, false)
12941295 require.NoError(t, err)
@@ -1301,6 +1302,6 @@ func TestCommentsPerMergeRequestAreBounded(t *testing.T) {
13011302 r.commit(author, "o.txt", "o\n", "other")
13021303 r.push("other")
13031304 other := r.open("other")
1304- _, err = r.mrs.AddComment(r.ctx, mrW, r.repo, other.Number, app.AddCommentParams{Text: "fine"})
1305+ _, err = r.mrs.AddComment(r.ctx, mrW, r.repo, other.Number, ports.AddCommentParams{Text: "fine"})
13051306 require.NoError(t, err)
13061307 }
M internal/app/mergerequests_test.go +5 -5
@@ -12,8 +12,8 @@ import (
1212 "github.com/stretchr/testify/require"
1313
1414 "bnema.dev/ermite/internal/app"
15- appmocks "bnema.dev/ermite/internal/app/mocks"
1615 "bnema.dev/ermite/internal/domain"
16+ "bnema.dev/ermite/internal/ports"
1717 "bnema.dev/ermite/internal/ports/mocks"
1818 )
1919
@@ -37,7 +37,7 @@ func newMRUnit(t *testing.T) *mrUnit {
3737 }
3838 clock := mocks.NewClock(t)
3939 clock.EXPECT().Now().Return(now).Maybe()
40- sched := appmocks.NewMaintenanceScheduler(t)
40+ sched := mocks.NewMaintenanceScheduler(t)
4141 sched.EXPECT().Schedule(mock.Anything, mock.Anything).Return().Maybe()
4242 u.mrs = app.NewMergeRequests(app.MergeRequestsConfig{
4343 Meta: u.meta, Store: u.store, Repos: u.repos, Git: u.git,
@@ -51,7 +51,7 @@ func newMRUnit(t *testing.T) *mrUnit {
5151 func TestCreateReturnsGitFailuresAsErrors(t *testing.T) {
5252 t.Parallel()
5353 boom := errors.New("fatal: /srv/ermite/repos/proj.git: permission denied")
54- params := app.CreateMergeRequestParams{Title: "t", Source: "feat", Target: "main"}
54+ params := ports.CreateMergeRequestParams{Title: "t", Source: "feat", Target: "main"}
5555
5656 t.Run("reading a branch", func(t *testing.T) {
5757 t.Parallel()
@@ -70,7 +70,7 @@ func TestCreateReturnsGitFailuresAsErrors(t *testing.T) {
7070 u := newMRUnit(t)
7171 u.meta.EXPECT().Get(mock.Anything, u.repo.Name).Return(u.repo, nil)
7272 u.repos.EXPECT().DefaultBranch(mock.Anything, u.repo.Name).Return("", false, boom)
73- _, err := u.mrs.Create(context.Background(), domain.Root(), u.repo.Name, app.CreateMergeRequestParams{Title: "t", Source: "feat"})
73+ _, err := u.mrs.Create(context.Background(), domain.Root(), u.repo.Name, ports.CreateMergeRequestParams{Title: "t", Source: "feat"})
7474 require.ErrorIs(t, err, boom)
7575 var fe *domain.InvalidFieldsError
7676 assert.False(t, errors.As(err, &fe), "not a field violation: %v", err)
@@ -109,7 +109,7 @@ func TestMergeReturnsTheFailureToReadTheSettings(t *testing.T) {
109109 u.store.EXPECT().Get(mock.Anything, u.repo.Name, int64(1)).Return(domain.MergeRequest{
110110 Repo: u.repo.Name, Number: 1, State: domain.MRStateOpen, Source: "feat", Target: "main",
111111 }, nil)
112- _, err := u.mrs.Merge(context.Background(), domain.Root(), u.repo.Name, 1, app.MergeParams{HeadID: someID(t)})
112+ _, err := u.mrs.Merge(context.Background(), domain.Root(), u.repo.Name, 1, ports.MergeParams{HeadID: someID(t)})
113113 require.ErrorIs(t, err, tc.want)
114114 if tc.want == boom {
115115 assert.NotErrorIs(t, err, domain.ErrNotFound)
M internal/app/paging.go +11 -24
@@ -11,6 +11,7 @@ import (
1111 "strings"
1212
1313 "bnema.dev/ermite/internal/domain"
14+ "bnema.dev/ermite/internal/ports"
1415 )
1516
1617 const (
@@ -83,31 +84,24 @@ func pageAfter[T any](items []T, size int, after func(T) bool, token func(T) str
8384 return page, ""
8485 }
8586
86-// RepoPage is a page of repositories ordered by name.
87-type RepoPage struct {
88- Repos []domain.Repo
89- // NextPageToken is empty on the last page.
90- NextPageToken string
91-}
92-
9387 // ListPage returns a page of the repositories the principal may read, ordered
9488 // by name. size is 0 for DefaultPageSize and lowered to MaxRepoPageSize; the page
9589 // token names the last repository of the previous page, so repositories that
9690 // appear or disappear between two pages never shift the others.
97-func (r *Repos) ListPage(ctx context.Context, p domain.Principal, size int, token string) (RepoPage, error) {
91+func (r *Repos) ListPage(ctx context.Context, p domain.Principal, size int, token string) (ports.RepoPage, error) {
9892 n, err := pageSizeUpTo(size, MaxRepoPageSize)
9993 if err != nil {
100- return RepoPage{}, err
94+ return ports.RepoPage{}, err
10195 }
10296 after, err := decodeRepoToken(token)
10397 if err != nil {
104- return RepoPage{}, err
98+ return ports.RepoPage{}, err
10599 }
106100 all, err := r.List(ctx, p)
107101 if err != nil {
108- return RepoPage{}, err
102+ return ports.RepoPage{}, err
109103 }
110- var page RepoPage
104+ var page ports.RepoPage
111105 page.Repos, page.NextPageToken = pageAfter(all, n,
112106 func(repo domain.Repo) bool { return after == "" || repo.Name.String() > after },
113107 func(repo domain.Repo) string { return encodeRepoToken(repo.Name) })
@@ -130,28 +124,21 @@ func decodeRepoToken(tok string) (string, error) {
130124 return raw, nil
131125 }
132126
133-// TokenPage is a page of stored tokens, newest first.
134-type TokenPage struct {
135- Tokens []domain.Token
136- // NextPageToken is empty on the last page.
137- NextPageToken string
138-}
139-
140127 // ListPage returns a page of the tokens List returns, newest first (then by
141128 // id). size and the page token work like Repos.ListPage; the token names the
142129 // last token of the previous page.
143-func (t *Tokens) ListPage(ctx context.Context, p domain.Principal, size int, token string) (TokenPage, error) {
130+func (t *Tokens) ListPage(ctx context.Context, p domain.Principal, size int, token string) (ports.TokenPage, error) {
144131 n, err := pageSize(size)
145132 if err != nil {
146- return TokenPage{}, err
133+ return ports.TokenPage{}, err
147134 }
148135 after, err := decodeTokenCursor(token)
149136 if err != nil {
150- return TokenPage{}, err
137+ return ports.TokenPage{}, err
151138 }
152139 all, err := t.List(ctx, p)
153140 if err != nil {
154- return TokenPage{}, err
141+ return ports.TokenPage{}, err
155142 }
156143 slices.SortFunc(all, func(a, b domain.Token) int {
157144 if c := b.CreatedAt.Compare(a.CreatedAt); c != 0 {
@@ -159,7 +146,7 @@ func (t *Tokens) ListPage(ctx context.Context, p domain.Principal, size int, tok
159146 }
160147 return strings.Compare(b.ID, a.ID)
161148 })
162- var page TokenPage
149+ var page ports.TokenPage
163150 page.Tokens, page.NextPageToken = pageAfter(all, n,
164151 func(tok domain.Token) bool { return after == nil || tokenAfter(tok, *after) },
165152 encodeTokenCursor)
M internal/app/pushes.go +9 -15
@@ -18,17 +18,6 @@ import (
1818 // pushRetries bounds the retries of a metadata update that loses a race.
1919 const pushRetries = 5
2020
21-// MaintenanceScheduler queues repository maintenance. *Maintainer fits.
22-type MaintenanceScheduler interface {
23- Schedule(ctx context.Context, name domain.RepoName)
24-}
25-
26-// Revalidator confirms that a principal obtained earlier is still
27-// authorized. *Auth fits.
28-type Revalidator interface {
29- Revalidate(ctx context.Context, p domain.Principal) error
30-}
31-
3221 // Pushes are the use cases around a Git push.
3322 type Pushes struct {
3423 meta ports.RepoMetaStore
@@ -36,15 +25,20 @@ type Pushes struct {
3625 git ports.GitRepos
3726 bus ports.EventBus
3827 clock ports.Clock
39- maint MaintenanceScheduler
40- auth Revalidator
28+ maint ports.MaintenanceScheduler
29+ auth ports.Revalidator
4130 fresh ports.RepoFreshener // nil: the reader needs no telling
4231 }
4332
33+var (
34+ _ ports.PushService = (*Pushes)(nil)
35+ _ ports.PushHookService = (*Pushes)(nil)
36+)
37+
4438 // NewPushes builds the push use cases. Every argument is required: auth
4539 // re-validates the token of a push when its pre-receive hook runs, maint
4640 // queues the maintenance that follows a push.
47-func NewPushes(meta ports.RepoMetaStore, repos *Repos, git ports.GitRepos, bus ports.EventBus, clock ports.Clock, maint MaintenanceScheduler, auth Revalidator) *Pushes {
41+func NewPushes(meta ports.RepoMetaStore, repos *Repos, git ports.GitRepos, bus ports.EventBus, clock ports.Clock, maint ports.MaintenanceScheduler, auth ports.Revalidator) *Pushes {
4842 return &Pushes{meta: meta, repos: repos, git: git, bus: bus, clock: clock, maint: maint, auth: auth}
4943 }
5044
@@ -78,7 +72,7 @@ func (u *Pushes) Open(ctx context.Context, p domain.Principal, name domain.RepoN
7872 if !create || !canWrite(p, name) {
7973 return domain.ErrNotFound
8074 }
81- _, err = u.repos.Create(ctx, p, CreateParams{Name: name, Visibility: domain.VisibilityPrivate})
75+ _, err = u.repos.Create(ctx, p, ports.CreateRepoParams{Name: name, Visibility: domain.VisibilityPrivate})
8276 switch {
8377 case err == nil:
8478 return nil // Create published RepoCreated
M internal/app/pushes_test.go +2 -3
@@ -13,7 +13,6 @@ import (
1313 "github.com/stretchr/testify/require"
1414
1515 "bnema.dev/ermite/internal/app"
16- appmocks "bnema.dev/ermite/internal/app/mocks"
1716 "bnema.dev/ermite/internal/domain"
1817 "bnema.dev/ermite/internal/ports"
1918 "bnema.dev/ermite/internal/ports/mocks"
@@ -25,7 +24,7 @@ type pushFixture struct {
2524 bus *mocks.EventBus
2625 clock *mocks.Clock
2726 tokens *mocks.TokenStore
28- sched *appmocks.MaintenanceScheduler
27+ sched *mocks.MaintenanceScheduler
2928 pushes *app.Pushes
3029 }
3130
@@ -37,7 +36,7 @@ func newPushFixture(t *testing.T) *pushFixture {
3736 bus: mocks.NewEventBus(t),
3837 clock: mocks.NewClock(t),
3938 tokens: mocks.NewTokenStore(t),
40- sched: appmocks.NewMaintenanceScheduler(t),
39+ sched: mocks.NewMaintenanceScheduler(t),
4140 }
4241 f.clock.EXPECT().Now().Return(now).Maybe()
4342 repos := app.NewRepos(f.meta, f.git, f.bus, f.clock)
M internal/app/repos.go +3 -9
@@ -23,20 +23,14 @@ type Repos struct {
2323 clock ports.Clock
2424 }
2525
26+var _ ports.RepoService = (*Repos)(nil)
27+
2628 // NewRepos builds the repository use cases over their ports. Changes are
2729 // published on bus.
2830 func NewRepos(meta ports.RepoMetaStore, git ports.GitRepos, bus ports.EventBus, clock ports.Clock) *Repos {
2931 return &Repos{meta: meta, git: git, bus: bus, clock: clock}
3032 }
3133
32-// CreateParams are the settings of a new repository.
33-type CreateParams struct {
34- Name domain.RepoName
35- Description string
36- // Visibility defaults to private when empty.
37- Visibility domain.Visibility
38-}
39-
4034 // Create makes a repository and publishes RepoCreated: it stages a bare repository, records the
4135 // metadata (which fails with domain.ErrAlreadyExists if the name is taken,
4236 // also when a concurrent Create wins) and publishes the directory. A failure
@@ -45,7 +39,7 @@ type CreateParams struct {
4539 //
4640 // The principal needs repo:write on the name: anonymous callers get
4741 // domain.ErrUnauthenticated, other callers without it domain.ErrForbidden.
48-func (r *Repos) Create(ctx context.Context, p domain.Principal, params CreateParams) (domain.Repo, error) {
42+func (r *Repos) Create(ctx context.Context, p domain.Principal, params ports.CreateRepoParams) (domain.Repo, error) {
4943 if params.Name.IsZero() {
5044 return domain.Repo{}, fmt.Errorf("%w: empty name", domain.ErrInvalidName)
5145 }
M internal/app/repos_integration_test.go +2 -1
@@ -20,6 +20,7 @@ import (
2020 "bnema.dev/ermite/internal/adapters/kvstore"
2121 "bnema.dev/ermite/internal/app"
2222 "bnema.dev/ermite/internal/domain"
23+ "bnema.dev/ermite/internal/ports"
2324 )
2425
2526 type fixedClock struct{}
@@ -56,7 +57,7 @@ func TestCreateConcurrentSameNameRealAdapters(t *testing.T) {
5657 wg.Add(1)
5758 go func() {
5859 defer wg.Done()
59- _, err := repos.Create(ctx, domain.Root(), app.CreateParams{Name: n})
60+ _, err := repos.Create(ctx, domain.Root(), ports.CreateRepoParams{Name: n})
6061 errs <- err
6162 }()
6263 }
M internal/app/repos_test.go +11 -11
@@ -63,7 +63,7 @@ func TestCreate(t *testing.T) {
6363 f.git.EXPECT().Publish(ctx, ports.StagedID("s1"), n).Return(nil)
6464 f.bus.EXPECT().Publish(ctx, domain.RepoCreated{Repo: n, Visibility: domain.VisibilityPrivate, At: now}).Return().Once()
6565
66- got, err := f.repos.Create(ctx, domain.Root(), app.CreateParams{Name: n, Description: "d"})
66+ got, err := f.repos.Create(ctx, domain.Root(), ports.CreateRepoParams{Name: n, Description: "d"})
6767 require.NoError(t, err)
6868 assert.Equal(t, want, got)
6969 })
@@ -71,7 +71,7 @@ func TestCreate(t *testing.T) {
7171 t.Run("anonymous is refused before anything runs", func(t *testing.T) {
7272 t.Parallel()
7373 f := newFixture(t)
74- _, err := f.repos.Create(ctx, domain.Anonymous(), app.CreateParams{Name: name(t, "tools")})
74+ _, err := f.repos.Create(ctx, domain.Anonymous(), ports.CreateRepoParams{Name: name(t, "tools")})
7575 assert.ErrorIs(t, err, domain.ErrUnauthenticated)
7676 })
7777
@@ -81,7 +81,7 @@ func TestCreate(t *testing.T) {
8181 tokenPrincipal(domain.ScopeRepoWrite, "other"),
8282 } {
8383 f := newFixture(t)
84- _, err := f.repos.Create(ctx, p, app.CreateParams{Name: name(t, "tools")})
84+ _, err := f.repos.Create(ctx, p, ports.CreateRepoParams{Name: name(t, "tools")})
8585 assert.ErrorIs(t, err, domain.ErrForbidden)
8686 }
8787 })
@@ -94,21 +94,21 @@ func TestCreate(t *testing.T) {
9494 f.meta.EXPECT().Create(ctx, mock.Anything).Return(nil)
9595 f.git.EXPECT().Publish(ctx, ports.StagedID("s1"), n).Return(nil)
9696 f.bus.EXPECT().Publish(ctx, mock.Anything).Return().Once()
97- _, err := f.repos.Create(ctx, tokenPrincipal(domain.ScopeRepoWrite, "tools"), app.CreateParams{Name: n})
97+ _, err := f.repos.Create(ctx, tokenPrincipal(domain.ScopeRepoWrite, "tools"), ports.CreateRepoParams{Name: n})
9898 require.NoError(t, err)
9999 })
100100
101101 t.Run("zero name is invalid", func(t *testing.T) {
102102 t.Parallel()
103103 f := newFixture(t)
104- _, err := f.repos.Create(ctx, domain.Root(), app.CreateParams{})
104+ _, err := f.repos.Create(ctx, domain.Root(), ports.CreateRepoParams{})
105105 assert.ErrorIs(t, err, domain.ErrInvalidName)
106106 })
107107
108108 t.Run("unknown visibility is invalid", func(t *testing.T) {
109109 t.Parallel()
110110 f := newFixture(t)
111- _, err := f.repos.Create(ctx, domain.Root(), app.CreateParams{Name: name(t, "tools"), Visibility: "secret"})
111+ _, err := f.repos.Create(ctx, domain.Root(), ports.CreateRepoParams{Name: name(t, "tools"), Visibility: "secret"})
112112 assert.ErrorIs(t, err, domain.ErrInvalidVisibility)
113113 })
114114
@@ -116,7 +116,7 @@ func TestCreate(t *testing.T) {
116116 t.Parallel()
117117 f := newFixture(t)
118118 f.git.EXPECT().Stage(ctx).Return("", errBoom)
119- _, err := f.repos.Create(ctx, domain.Root(), app.CreateParams{Name: name(t, "tools")})
119+ _, err := f.repos.Create(ctx, domain.Root(), ports.CreateRepoParams{Name: name(t, "tools")})
120120 assert.ErrorIs(t, err, errBoom)
121121 })
122122
@@ -126,7 +126,7 @@ func TestCreate(t *testing.T) {
126126 f.git.EXPECT().Stage(ctx).Return(ports.StagedID("s1"), nil)
127127 f.meta.EXPECT().Create(ctx, mock.Anything).Return(domain.ErrAlreadyExists)
128128 f.git.EXPECT().Discard(mock.Anything, ports.StagedID("s1")).Return(nil)
129- _, err := f.repos.Create(ctx, domain.Root(), app.CreateParams{Name: name(t, "tools")})
129+ _, err := f.repos.Create(ctx, domain.Root(), ports.CreateRepoParams{Name: name(t, "tools")})
130130 assert.ErrorIs(t, err, domain.ErrAlreadyExists)
131131 })
132132
@@ -139,7 +139,7 @@ func TestCreate(t *testing.T) {
139139 f.git.EXPECT().Publish(ctx, ports.StagedID("s1"), n).Return(domain.ErrAlreadyExists)
140140 f.meta.EXPECT().Delete(mock.Anything, n).Return(nil)
141141 f.git.EXPECT().Discard(mock.Anything, ports.StagedID("s1")).Return(nil)
142- _, err := f.repos.Create(ctx, domain.Root(), app.CreateParams{Name: n})
142+ _, err := f.repos.Create(ctx, domain.Root(), ports.CreateRepoParams{Name: n})
143143 assert.ErrorIs(t, err, domain.ErrAlreadyExists)
144144 })
145145
@@ -152,7 +152,7 @@ func TestCreate(t *testing.T) {
152152 f.git.EXPECT().Publish(ctx, ports.StagedID("s1"), n).Return(errBoom)
153153 f.meta.EXPECT().Delete(mock.Anything, n).Return(nil)
154154 f.git.EXPECT().Discard(mock.Anything, ports.StagedID("s1")).Return(nil)
155- _, err := f.repos.Create(ctx, domain.Root(), app.CreateParams{Name: n})
155+ _, err := f.repos.Create(ctx, domain.Root(), ports.CreateRepoParams{Name: n})
156156 assert.ErrorIs(t, err, errBoom)
157157 })
158158
@@ -174,7 +174,7 @@ func TestCreate(t *testing.T) {
174174 f.git.EXPECT().Discard(mock.Anything, ports.StagedID("s1")).RunAndReturn(func(c context.Context, _ ports.StagedID) error {
175175 return c.Err()
176176 })
177- _, err := f.repos.Create(cctx, domain.Root(), app.CreateParams{Name: n})
177+ _, err := f.repos.Create(cctx, domain.Root(), ports.CreateRepoParams{Name: n})
178178 assert.ErrorIs(t, err, errBoom)
179179 })
180180 }
M internal/app/repos_write.go +6 -25
@@ -12,6 +12,7 @@ import (
1212 "github.com/bnema/zerowrap"
1313
1414 "bnema.dev/ermite/internal/domain"
15+ "bnema.dev/ermite/internal/ports"
1516 )
1617
1718 // MaxDescriptionLen is the longest repository description, in bytes.
@@ -33,34 +34,14 @@ func validateDescription(s string) error {
3334 return nil
3435 }
3536
36-// RepoView is a repository with the Git state that belongs in its summary.
37-type RepoView struct {
38- domain.Repo
39- // DefaultBranch is the branch HEAD points to, even when it does not exist
40- // yet; empty when HEAD is detached or the directory is missing.
41- DefaultBranch string
42-}
43-
4437 // View adds the default branch to a repository the caller has already been
4538 // allowed to see. A repository without a directory has no default branch.
46-func (r *Repos) View(ctx context.Context, repo domain.Repo) (RepoView, error) {
39+func (r *Repos) View(ctx context.Context, repo domain.Repo) (ports.RepoView, error) {
4740 branch, err := r.git.HeadBranch(ctx, repo.Name)
4841 if err != nil && !errors.Is(err, domain.ErrNotFound) {
49- return RepoView{}, fmt.Errorf("read default branch of %q: %w", repo.Name, err)
42+ return ports.RepoView{}, fmt.Errorf("read default branch of %q: %w", repo.Name, err)
5043 }
51- return RepoView{Repo: repo, DefaultBranch: branch}, nil
52-}
53-
54-// UpdateParams are the changes of Update; a nil field is left alone.
55-type UpdateParams struct {
56- Description *string
57- Visibility *domain.Visibility
58- DefaultBranch *string
59- // AllowedMergeModes replaces the allowed merge modes; it must hold at
60- // least one known mode.
61- AllowedMergeModes *[]domain.MergeMode
62- // FailingChecksBlock replaces the setting of that name.
63- FailingChecksBlock *bool
44+ return ports.RepoView{Repo: repo, DefaultBranch: branch}, nil
6445 }
6546
6647 // Update changes the description, the visibility, the default branch and the
@@ -71,7 +52,7 @@ type UpdateParams struct {
7152 // The metadata is written first, then HEAD, and RepoUpdated is published after
7253 // both. When only the HEAD move fails, RepoUpdated is still published for the
7354 // stored metadata and the HEAD error is returned.
74-func (r *Repos) Update(ctx context.Context, p domain.Principal, name domain.RepoName, params UpdateParams) (domain.Repo, error) {
55+func (r *Repos) Update(ctx context.Context, p domain.Principal, name domain.RepoName, params ports.UpdateRepoParams) (domain.Repo, error) {
7556 repo, err := lookupRepo(ctx, r.meta, name)
7657 if err != nil {
7758 return domain.Repo{}, err
@@ -158,7 +139,7 @@ func (r *Repos) Update(ctx context.Context, p domain.Principal, name domain.Repo
158139
159140 // updateMeta applies the metadata fields, retrying when a write (a push
160141 // recording pushed_at) gets in between.
161-func (r *Repos) updateMeta(ctx context.Context, name domain.RepoName, params UpdateParams) (domain.Repo, error) {
142+func (r *Repos) updateMeta(ctx context.Context, name domain.RepoName, params ports.UpdateRepoParams) (domain.Repo, error) {
162143 for range pushRetries {
163144 repo, err := r.meta.Get(ctx, name)
164145 if err != nil {
M internal/app/repos_write_test.go +11 -10
@@ -15,6 +15,7 @@ import (
1515
1616 "bnema.dev/ermite/internal/app"
1717 "bnema.dev/ermite/internal/domain"
18+ "bnema.dev/ermite/internal/ports"
1819 )
1920
2021 func ptr[T any](v T) *T { return &v }
@@ -36,7 +37,7 @@ func TestUpdate(t *testing.T) {
3637 Repo: n, At: now, Visibility: domain.VisibilityPublic, Previous: domain.VisibilityPrivate,
3738 }).Return().Once()
3839
39- got, err := f.repos.Update(ctx, admin, n, app.UpdateParams{
40+ got, err := f.repos.Update(ctx, admin, n, ports.UpdateRepoParams{
4041 Description: ptr("new"), Visibility: ptr(domain.VisibilityPublic),
4142 })
4243 require.NoError(t, err)
@@ -50,7 +51,7 @@ func TestUpdate(t *testing.T) {
5051 f.meta.EXPECT().Update(ctx, mock.Anything).Return(domain.ErrConflict).Once()
5152 f.meta.EXPECT().Update(ctx, mock.Anything).Return(nil).Once()
5253 f.bus.EXPECT().Publish(ctx, mock.Anything).Return().Once()
53- _, err := f.repos.Update(ctx, admin, n, app.UpdateParams{Description: ptr("x")})
54+ _, err := f.repos.Update(ctx, admin, n, ports.UpdateRepoParams{Description: ptr("x")})
5455 require.NoError(t, err)
5556 })
5657
@@ -61,7 +62,7 @@ func TestUpdate(t *testing.T) {
6162 f.git.EXPECT().HasBranch(ctx, n, "dev").Return(true, nil)
6263 f.git.EXPECT().SetDefaultBranch(ctx, n, "dev").Return(nil)
6364 f.bus.EXPECT().Publish(ctx, mock.Anything).Return().Once()
64- got, err := f.repos.Update(ctx, admin, n, app.UpdateParams{DefaultBranch: ptr("dev")})
65+ got, err := f.repos.Update(ctx, admin, n, ports.UpdateRepoParams{DefaultBranch: ptr("dev")})
6566 require.NoError(t, err)
6667 assert.Equal(t, priv, got)
6768 })
@@ -81,7 +82,7 @@ func TestUpdate(t *testing.T) {
8182 return nil
8283 })
8384 f.bus.EXPECT().Publish(ctx, mock.Anything).Run(func(context.Context, domain.Event) { order = append(order, "event") }).Return().Once()
84- _, err := f.repos.Update(ctx, admin, n, app.UpdateParams{Description: ptr("x"), DefaultBranch: ptr("dev")})
85+ _, err := f.repos.Update(ctx, admin, n, ports.UpdateRepoParams{Description: ptr("x"), DefaultBranch: ptr("dev")})
8586 require.NoError(t, err)
8687 assert.Equal(t, []string{"meta", "head", "event"}, order)
8788 })
@@ -98,7 +99,7 @@ func TestUpdate(t *testing.T) {
9899 f.bus.EXPECT().Publish(ctx, domain.RepoUpdated{
99100 Repo: n, At: now, Visibility: domain.VisibilityPrivate, Previous: domain.VisibilityPublic,
100101 }).Return().Once()
101- _, err := f.repos.Update(ctx, admin, n, app.UpdateParams{Visibility: ptr(domain.VisibilityPrivate), DefaultBranch: ptr("dev")})
102+ _, err := f.repos.Update(ctx, admin, n, ports.UpdateRepoParams{Visibility: ptr(domain.VisibilityPrivate), DefaultBranch: ptr("dev")})
102103 assert.ErrorIs(t, err, boom)
103104 })
104105
@@ -109,7 +110,7 @@ func TestUpdate(t *testing.T) {
109110 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)
110111 f.git.EXPECT().HasBranch(ctx, n, "dev").Return(true, nil)
111112 f.git.EXPECT().SetDefaultBranch(ctx, n, "dev").Return(boom)
112- _, err := f.repos.Update(ctx, admin, n, app.UpdateParams{DefaultBranch: ptr("dev")})
113+ _, err := f.repos.Update(ctx, admin, n, ports.UpdateRepoParams{DefaultBranch: ptr("dev")})
113114 assert.ErrorIs(t, err, boom)
114115 })
115116
@@ -118,12 +119,12 @@ func TestUpdate(t *testing.T) {
118119 f := newFixture(t)
119120 f.meta.EXPECT().Get(ctx, n).Return(priv, nil)
120121 f.git.EXPECT().HasBranch(ctx, n, "nope").Return(false, nil)
121- _, err := f.repos.Update(ctx, admin, n, app.UpdateParams{Description: ptr("x"), DefaultBranch: ptr("nope")})
122+ _, err := f.repos.Update(ctx, admin, n, ports.UpdateRepoParams{Description: ptr("x"), DefaultBranch: ptr("nope")})
122123 assert.ErrorIs(t, err, domain.ErrPreconditionFailed)
123124 })
124125
125126 t.Run("invalid input changes nothing", func(t *testing.T) {
126- for name, params := range map[string]app.UpdateParams{
127+ for name, params := range map[string]ports.UpdateRepoParams{
127128 "empty": {},
128129 "empty branch": {DefaultBranch: ptr("")},
129130 "multi-line": {Description: ptr("a\nb")},
@@ -157,7 +158,7 @@ func TestUpdate(t *testing.T) {
157158 t.Parallel()
158159 f := newFixture(t)
159160 f.meta.EXPECT().Get(ctx, n).Return(tt.repo, nil)
160- _, err := f.repos.Update(ctx, tt.p, n, app.UpdateParams{Description: ptr("x")})
161+ _, err := f.repos.Update(ctx, tt.p, n, ports.UpdateRepoParams{Description: ptr("x")})
161162 assert.ErrorIs(t, err, tt.want)
162163 err = f.repos.Delete(ctx, tt.p, n)
163164 assert.ErrorIs(t, err, tt.want)
@@ -168,7 +169,7 @@ func TestUpdate(t *testing.T) {
168169 t.Parallel()
169170 f := newFixture(t)
170171 f.meta.EXPECT().Get(ctx, n).Return(domain.Repo{}, domain.ErrNotFound)
171- _, err := f.repos.Update(ctx, domain.Root(), n, app.UpdateParams{Description: ptr("x")})
172+ _, err := f.repos.Update(ctx, domain.Root(), n, ports.UpdateRepoParams{Description: ptr("x")})
172173 assert.ErrorIs(t, err, domain.ErrNotFound)
173174 })
174175 }
M internal/app/sessions.go +7 -12
@@ -39,13 +39,6 @@ const (
3939 LoginPath = "/-/login/"
4040 )
4141
42-// LoginLink is a login link as the owner receives it.
43-type LoginLink struct {
44- // URL holds the secret: show it once, never log it.
45- URL string
46- ExpiresAt time.Time
47-}
48-
4942 // Sessions are the use cases of the owner's browser sessions: login links,
5043 // session tokens and signing out everywhere. Login links live in memory only,
5144 // so a restart voids them; the signing key and the epoch are in the store.
@@ -59,6 +52,8 @@ type Sessions struct {
5952 links map[[sha256.Size]byte]pendingLink // by hash of the nonce
6053 }
6154
55+var _ ports.SessionService = (*Sessions)(nil)
56+
6257 // pendingLink is a login link that waits to be used.
6358 type pendingLink struct {
6459 expires time.Time
@@ -95,13 +90,13 @@ func requireOwner(p domain.Principal) error {
9590 // created it: it is capped at that token's expiry (no cap for root, or a token
9691 // that never expires). It gives domain.ErrResourceExhausted when MaxPendingLoginLinks
9792 // links wait. The nonce is never logged.
98-func (s *Sessions) CreateLoginLink(ctx context.Context, p domain.Principal) (LoginLink, error) {
93+func (s *Sessions) CreateLoginLink(ctx context.Context, p domain.Principal) (ports.LoginLink, error) {
9994 if err := requireOwner(p); err != nil {
100- return LoginLink{}, err
95+ return ports.LoginLink{}, err
10196 }
10297 var raw [loginNonceBytes]byte
10398 if _, err := rand.Read(raw[:]); err != nil {
104- return LoginLink{}, fmt.Errorf("generate login link: %w", err)
99+ return ports.LoginLink{}, fmt.Errorf("generate login link: %w", err)
105100 }
106101 nonce := base64.RawURLEncoding.EncodeToString(raw[:])
107102 now := s.clock.Now()
@@ -115,14 +110,14 @@ func (s *Sessions) CreateLoginLink(ctx context.Context, p domain.Principal) (Log
115110 }
116111 if len(s.links) >= MaxPendingLoginLinks {
117112 s.mu.Unlock()
118- return LoginLink{}, fmt.Errorf("%w: %d login links are already pending, use one or wait a minute", domain.ErrResourceExhausted, MaxPendingLoginLinks)
113+ return ports.LoginLink{}, fmt.Errorf("%w: %d login links are already pending, use one or wait a minute", domain.ErrResourceExhausted, MaxPendingLoginLinks)
119114 }
120115 s.links[sha256.Sum256([]byte(nonce))] = pendingLink{expires: exp, sessionCap: p.ExpiresAt()}
121116 s.mu.Unlock()
122117
123118 log := zerowrap.FromCtx(ctx)
124119 log.Info().Str("created_by", p.Label()).Str("created_by_token", p.TokenID()).Time("expires_at", exp).Msg("login link created")
125- return LoginLink{URL: s.publicURL + LoginPath + nonce, ExpiresAt: exp.UTC()}, nil
120+ return ports.LoginLink{URL: s.publicURL + LoginPath + nonce, ExpiresAt: exp.UTC()}, nil
126121 }
127122
128123 // LoginLinkValid reports whether the nonce is a pending link, without using
M internal/app/signing.go +2 -0
@@ -14,6 +14,8 @@ type SigningKey struct {
1414 committer domain.Identity
1515 }
1616
17+var _ ports.SigningKeyService = (*SigningKey)(nil)
18+
1719 // NewSigningKey builds the use case over the signer and the identity recorded
1820 // as the committer of the signed commits.
1921 func NewSigningKey(signer ports.CommitSigner, committer domain.Identity) *SigningKey {
M internal/app/tokens.go +16 -27
@@ -40,30 +40,14 @@ type Tokens struct {
4040 sessions ports.SessionStore
4141 }
4242
43+var _ ports.TokenService = (*Tokens)(nil)
44+
4345 // NewTokens builds the token use cases over their ports. sessions is where
4446 // revoking a token with owner rights ends every web session (see Revoke).
4547 func NewTokens(store ports.TokenStore, clock ports.Clock, sessions ports.SessionStore) *Tokens {
4648 return &Tokens{store: store, clock: clock, sessions: sessions}
4749 }
4850
49-// CreateTokenParams are the settings of a new token.
50-type CreateTokenParams struct {
51- Label string
52- // Scopes must not be empty.
53- Scopes []domain.Scope
54- // Repos limits the token to these repositories; empty means every one.
55- Repos []domain.RepoName
56- // ExpiresAt is the zero time for a token that never expires.
57- ExpiresAt time.Time
58-}
59-
60-// CreatedToken is a new token with its secret, which is shown once and never
61-// stored.
62-type CreatedToken struct {
63- Token domain.Token
64- Secret string
65-}
66-
6751 // Create makes a stored token and returns it with its secret.
6852 //
6953 // The principal needs token:admin: anonymous callers get
@@ -75,20 +59,20 @@ type CreatedToken struct {
7559 // domain.ErrInvalidToken (this includes the labels "owner" and "root", which
7660 // are reserved, in any case and around spaces), an unknown scope
7761 // domain.ErrInvalidScope.
78-func (t *Tokens) Create(ctx context.Context, p domain.Principal, params CreateTokenParams) (CreatedToken, error) {
62+func (t *Tokens) Create(ctx context.Context, p domain.Principal, params ports.CreateTokenParams) (ports.CreatedToken, error) {
7963 if p.IsAnonymous() {
80- return CreatedToken{}, domain.ErrUnauthenticated
64+ return ports.CreatedToken{}, domain.ErrUnauthenticated
8165 }
8266 if !canManageTokens(p) {
83- return CreatedToken{}, fmt.Errorf("%w: creating tokens needs %s", domain.ErrForbidden, domain.ScopeTokenAdmin)
67+ return ports.CreatedToken{}, fmt.Errorf("%w: creating tokens needs %s", domain.ErrForbidden, domain.ScopeTokenAdmin)
8468 }
8569 now := t.clock.Now().UTC()
8670 tok, err := buildToken(params, now)
8771 if err != nil {
88- return CreatedToken{}, err
72+ return ports.CreatedToken{}, err
8973 }
9074 if err := checkNoEscalation(p, tok); err != nil {
91- return CreatedToken{}, err
75+ return ports.CreatedToken{}, err
9276 }
9377
9478 for range 3 {
@@ -99,7 +83,7 @@ func (t *Tokens) Create(ctx context.Context, p domain.Principal, params CreateTo
9983 continue // a random id or secret collided: practically never
10084 }
10185 if err != nil {
102- return CreatedToken{}, fmt.Errorf("store token: %w", err)
86+ return ports.CreatedToken{}, fmt.Errorf("store token: %w", err)
10387 }
10488 log := zerowrap.FromCtx(ctx)
10589 log.Info().
@@ -107,13 +91,13 @@ func (t *Tokens) Create(ctx context.Context, p domain.Principal, params CreateTo
10791 Int("scopes", len(tok.Scopes)).Int("repos", len(tok.Repos)).
10892 Str("created_by", p.Label()).Str("created_by_token", p.TokenID()).
10993 Msg("token created")
110- return CreatedToken{Token: tok, Secret: secret}, nil
94+ return ports.CreatedToken{Token: tok, Secret: secret}, nil
11195 }
112- return CreatedToken{}, errors.New("store token: could not find a free id")
96+ return ports.CreatedToken{}, errors.New("store token: could not find a free id")
11397 }
11498
11599 // buildToken validates the settings and returns the token without id and hash.
116-func buildToken(params CreateTokenParams, now time.Time) (domain.Token, error) {
100+func buildToken(params ports.CreateTokenParams, now time.Time) (domain.Token, error) {
117101 if params.Label == "" || len(params.Label) > MaxTokenLabelLen || !utf8.ValidString(params.Label) {
118102 return domain.Token{}, fmt.Errorf("%w: label must be 1 to %d bytes of UTF-8", domain.ErrInvalidToken, MaxTokenLabelLen)
119103 }
@@ -274,6 +258,11 @@ type Auth struct {
274258 clock ports.Clock
275259 }
276260
261+var (
262+ _ ports.AuthService = (*Auth)(nil)
263+ _ ports.Revalidator = (*Auth)(nil)
264+)
265+
277266 // NewAuth builds the authentication use case. rootHash is the SHA-256 of the
278267 // admin token.
279268 func NewAuth(rootHash domain.TokenHash, tokens ports.TokenStore, clock ports.Clock) *Auth {
M internal/app/tokens_test.go +35 -34
@@ -16,6 +16,7 @@ import (
1616
1717 "bnema.dev/ermite/internal/app"
1818 "bnema.dev/ermite/internal/domain"
19+ "bnema.dev/ermite/internal/ports"
1920 "bnema.dev/ermite/internal/ports/mocks"
2021 )
2122
@@ -65,7 +66,7 @@ func TestTokensCreateAllowsLabelsNearReserved(t *testing.T) {
6566 t.Parallel()
6667 f := newTokenFixture(t)
6768 f.store.EXPECT().Create(ctx, mock.Anything).Return(nil)
68- _, err := f.tokens.Create(ctx, domain.Root(), app.CreateTokenParams{Label: tt.label, Scopes: scopes(domain.ScopeRepoRead)})
69+ _, err := f.tokens.Create(ctx, domain.Root(), ports.CreateTokenParams{Label: tt.label, Scopes: scopes(domain.ScopeRepoRead)})
6970 assert.NoError(t, err)
7071 })
7172 }
@@ -82,7 +83,7 @@ func TestTokensCreate(t *testing.T) {
8283 f.store.EXPECT().Create(ctx, mock.Anything).Run(func(_ context.Context, tok domain.Token) { stored = tok }).Return(nil)
8384
8485 exp := now.Add(48 * time.Hour)
85- got, err := f.tokens.Create(ctx, domain.Root(), app.CreateTokenParams{
86+ got, err := f.tokens.Create(ctx, domain.Root(), ports.CreateTokenParams{
8687 Label: "ci",
8788 Scopes: scopes(domain.ScopeRepoWrite, domain.ScopeRepoRead, domain.ScopeRepoRead),
8889 Repos: names(t, "b", "a", "b"),
@@ -109,9 +110,9 @@ func TestTokensCreate(t *testing.T) {
109110 t.Parallel()
110111 f := newTokenFixture(t)
111112 f.store.EXPECT().Create(ctx, mock.Anything).Return(nil)
112- a, err := f.tokens.Create(ctx, domain.Root(), app.CreateTokenParams{Label: "a", Scopes: scopes(domain.ScopeRepoRead)})
113+ a, err := f.tokens.Create(ctx, domain.Root(), ports.CreateTokenParams{Label: "a", Scopes: scopes(domain.ScopeRepoRead)})
113114 require.NoError(t, err)
114- b, err := f.tokens.Create(ctx, domain.Root(), app.CreateTokenParams{Label: "a", Scopes: scopes(domain.ScopeRepoRead)})
115+ b, err := f.tokens.Create(ctx, domain.Root(), ports.CreateTokenParams{Label: "a", Scopes: scopes(domain.ScopeRepoRead)})
115116 require.NoError(t, err)
116117 assert.NotEqual(t, a.Secret, b.Secret)
117118 assert.NotEqual(t, a.Token.ID, b.Token.ID)
@@ -122,7 +123,7 @@ func TestTokensCreate(t *testing.T) {
122123 f := newTokenFixture(t)
123124 f.store.EXPECT().Create(ctx, mock.Anything).Return(domain.ErrAlreadyExists).Once()
124125 f.store.EXPECT().Create(ctx, mock.Anything).Return(nil).Once()
125- _, err := f.tokens.Create(ctx, domain.Root(), app.CreateTokenParams{Label: "a", Scopes: scopes(domain.ScopeRepoRead)})
126+ _, err := f.tokens.Create(ctx, domain.Root(), ports.CreateTokenParams{Label: "a", Scopes: scopes(domain.ScopeRepoRead)})
126127 require.NoError(t, err)
127128 })
128129
@@ -131,27 +132,27 @@ func TestTokensCreate(t *testing.T) {
131132 f := newTokenFixture(t)
132133 boom := errors.New("boom")
133134 f.store.EXPECT().Create(ctx, mock.Anything).Return(boom)
134- _, err := f.tokens.Create(ctx, domain.Root(), app.CreateTokenParams{Label: "a", Scopes: scopes(domain.ScopeRepoRead)})
135+ _, err := f.tokens.Create(ctx, domain.Root(), ports.CreateTokenParams{Label: "a", Scopes: scopes(domain.ScopeRepoRead)})
135136 assert.ErrorIs(t, err, boom)
136137 })
137138
138139 invalid := []struct {
139140 name string
140- params app.CreateTokenParams
141+ params ports.CreateTokenParams
141142 want error
142143 }{
143- {"empty label", app.CreateTokenParams{Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrInvalidToken},
144- {"long label", app.CreateTokenParams{Label: strings.Repeat("x", 101), Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrInvalidToken},
145- {"bad utf8 label", app.CreateTokenParams{Label: "\xff", Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrInvalidToken},
146- {"reserved owner", app.CreateTokenParams{Label: "owner", Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrInvalidToken},
147- {"reserved root", app.CreateTokenParams{Label: "root", Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrInvalidToken},
148- {"reserved, upper case", app.CreateTokenParams{Label: "OWNER", Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrInvalidToken},
149- {"reserved, mixed case", app.CreateTokenParams{Label: "Root", Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrInvalidToken},
150- {"reserved, padded", app.CreateTokenParams{Label: " \towner \n", Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrInvalidToken},
151- {"no scopes", app.CreateTokenParams{Label: "x"}, domain.ErrInvalidToken},
152- {"unknown scope", app.CreateTokenParams{Label: "x", Scopes: scopes("nope")}, domain.ErrInvalidScope},
153- {"past expiry", app.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead), ExpiresAt: now}, domain.ErrInvalidToken},
154- {"zero repo name", app.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead), Repos: []domain.RepoName{{}}}, domain.ErrInvalidToken},
144+ {"empty label", ports.CreateTokenParams{Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrInvalidToken},
145+ {"long label", ports.CreateTokenParams{Label: strings.Repeat("x", 101), Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrInvalidToken},
146+ {"bad utf8 label", ports.CreateTokenParams{Label: "\xff", Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrInvalidToken},
147+ {"reserved owner", ports.CreateTokenParams{Label: "owner", Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrInvalidToken},
148+ {"reserved root", ports.CreateTokenParams{Label: "root", Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrInvalidToken},
149+ {"reserved, upper case", ports.CreateTokenParams{Label: "OWNER", Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrInvalidToken},
150+ {"reserved, mixed case", ports.CreateTokenParams{Label: "Root", Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrInvalidToken},
151+ {"reserved, padded", ports.CreateTokenParams{Label: " \towner \n", Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrInvalidToken},
152+ {"no scopes", ports.CreateTokenParams{Label: "x"}, domain.ErrInvalidToken},
153+ {"unknown scope", ports.CreateTokenParams{Label: "x", Scopes: scopes("nope")}, domain.ErrInvalidScope},
154+ {"past expiry", ports.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead), ExpiresAt: now}, domain.ErrInvalidToken},
155+ {"zero repo name", ports.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead), Repos: []domain.RepoName{{}}}, domain.ErrInvalidToken},
155156 }
156157 for _, tt := range invalid {
157158 t.Run("invalid/"+tt.name, func(t *testing.T) {
@@ -179,37 +180,37 @@ func TestTokensCreateNoEscalation(t *testing.T) {
179180 tests := []struct {
180181 name string
181182 principal domain.Principal
182- params app.CreateTokenParams
183+ params ports.CreateTokenParams
183184 want error // nil = allowed
184185 }{
185- {"anonymous", domain.Anonymous(), app.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrUnauthenticated},
186+ {"anonymous", domain.Anonymous(), ports.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrUnauthenticated},
186187 {"token without token:admin",
187188 domain.TokenPrincipal(domain.Token{Scopes: scopes(domain.ScopeRepoWrite)}),
188- app.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrForbidden},
189+ ports.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrForbidden},
189190 {"subset of scopes", admin(nil, time.Time{}),
190- app.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead)}, nil},
191+ ports.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead)}, nil},
191192 {"same scopes incl. token:admin", admin(nil, time.Time{}),
192- app.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeTokenAdmin, domain.ScopeRepoWrite)}, nil},
193+ ports.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeTokenAdmin, domain.ScopeRepoWrite)}, nil},
193194 {"scope it lacks", admin(nil, time.Time{}),
194- app.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead, domain.ScopeRepoAdmin)}, domain.ErrForbidden},
195+ ports.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead, domain.ScopeRepoAdmin)}, domain.ErrForbidden},
195196 {"limited creator, same repos", admin(names(t, "a", "b"), time.Time{}),
196- app.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead), Repos: names(t, "a")}, nil},
197+ ports.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead), Repos: names(t, "a")}, nil},
197198 {"limited creator, repo outside its list", admin(names(t, "a"), time.Time{}),
198- app.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead), Repos: names(t, "a", "z")}, domain.ErrForbidden},
199+ ports.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead), Repos: names(t, "a", "z")}, domain.ErrForbidden},
199200 {"limited creator, unlimited child", admin(names(t, "a"), time.Time{}),
200- app.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrForbidden},
201+ ports.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrForbidden},
201202 {"unlimited creator may limit", admin(nil, time.Time{}),
202- app.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead), Repos: names(t, "z")}, nil},
203+ ports.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead), Repos: names(t, "z")}, nil},
203204 {"expiring creator, child never expires", admin(nil, exp),
204- app.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrForbidden},
205+ ports.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead)}, domain.ErrForbidden},
205206 {"expiring creator, child outlasts", admin(nil, exp),
206- app.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead), ExpiresAt: exp.Add(time.Second)}, domain.ErrForbidden},
207+ ports.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead), ExpiresAt: exp.Add(time.Second)}, domain.ErrForbidden},
207208 {"expiring creator, child within", admin(nil, exp),
208- app.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead), ExpiresAt: exp.Add(-time.Second)}, nil},
209+ ports.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead), ExpiresAt: exp.Add(-time.Second)}, nil},
209210 {"expiring creator, child same expiry", admin(nil, exp),
210- app.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead), ExpiresAt: exp}, nil},
211+ ports.CreateTokenParams{Label: "x", Scopes: scopes(domain.ScopeRepoRead), ExpiresAt: exp}, nil},
211212 {"root: everything", domain.Root(),
212- app.CreateTokenParams{Label: "x", Scopes: domain.AllScopes()}, nil},
213+ ports.CreateTokenParams{Label: "x", Scopes: domain.AllScopes()}, nil},
213214 }
214215 for _, tt := range tests {
215216 t.Run(tt.name, func(t *testing.T) {
M internal/clientlimit/limiter.go +4 -0
@@ -16,6 +16,8 @@ import (
1616 "time"
1717
1818 "github.com/bnema/zerowrap"
19+
20+ "bnema.dev/ermite/internal/ports"
1921 )
2022
2123 // Limiter defaults: 10 failed authentications in 10 minutes block the client
@@ -54,6 +56,8 @@ type Limiter struct {
5456 lastSweep time.Time
5557 }
5658
59+var _ ports.FailureLimiter = (*Limiter)(nil)
60+
5761 // NewLimiter builds a limiter. now defaults to time.Now.
5862 func NewLimiter(now func() time.Time) *Limiter {
5963 if now == nil {
A internal/ports/dto.go +228 -0
@@ -0,0 +1,228 @@
1+// SPDX-License-Identifier: AGPL-3.0-or-later
2+
3+package ports
4+
5+import (
6+ "time"
7+
8+ "bnema.dev/ermite/internal/domain"
9+)
10+
11+// The parameters and results of the use case interfaces (services.go). They
12+// live here so that the adapters that call the use cases and the use cases
13+// themselves share them without importing each other.
14+
15+// RepoPage is a page of repositories ordered by name.
16+type RepoPage struct {
17+ Repos []domain.Repo
18+ // NextPageToken is empty on the last page.
19+ NextPageToken string
20+}
21+
22+// RepoView is a repository with the Git state that belongs in its summary.
23+type RepoView struct {
24+ domain.Repo
25+ // DefaultBranch is the branch HEAD points to, even when it does not exist
26+ // yet; empty when HEAD is detached or the directory is missing.
27+ DefaultBranch string
28+}
29+
30+// CreateRepoParams are the settings of a new repository.
31+type CreateRepoParams struct {
32+ Name domain.RepoName
33+ Description string
34+ // Visibility defaults to private when empty.
35+ Visibility domain.Visibility
36+}
37+
38+// UpdateRepoParams are the changes of Update; a nil field is left alone.
39+type UpdateRepoParams struct {
40+ Description *string
41+ Visibility *domain.Visibility
42+ DefaultBranch *string
43+ // AllowedMergeModes replaces the allowed merge modes; it must hold at
44+ // least one known mode.
45+ AllowedMergeModes *[]domain.MergeMode
46+ // FailingChecksBlock replaces the setting of that name.
47+ FailingChecksBlock *bool
48+}
49+
50+// TokenPage is a page of stored tokens, newest first.
51+type TokenPage struct {
52+ Tokens []domain.Token
53+ // NextPageToken is empty on the last page.
54+ NextPageToken string
55+}
56+
57+// CreateTokenParams are the settings of a new token.
58+type CreateTokenParams struct {
59+ Label string
60+ // Scopes must not be empty.
61+ Scopes []domain.Scope
62+ // Repos limits the token to these repositories; empty means every one.
63+ Repos []domain.RepoName
64+ // ExpiresAt is the zero time for a token that never expires.
65+ ExpiresAt time.Time
66+}
67+
68+// CreatedToken is a new token with its secret, which is shown once and never
69+// stored.
70+type CreatedToken struct {
71+ Token domain.Token
72+ Secret string
73+}
74+
75+// ListRefsParams selects the refs to list.
76+type ListRefsParams struct {
77+ // Kinds restricts the result; empty means every kind.
78+ Kinds []domain.RefKind
79+ // PageSize is 0 for the default, and lowered to MaxRefPageSize.
80+ PageSize int
81+ // PageToken is the NextPageToken of the previous page, or empty.
82+ PageToken string
83+}
84+
85+// RefPage is a page of refs.
86+type RefPage struct {
87+ Refs []domain.Ref
88+ // NextPageToken is empty on the last page.
89+ NextPageToken string
90+}
91+
92+// ListCommitsParams selects a page of history.
93+type ListCommitsParams struct {
94+ // Revision is where the history starts; the zero value is HEAD. Ignored
95+ // when PageToken is set: the token pins the commit the first page started
96+ // from, so every page walks the same history even if a branch moves.
97+ Revision domain.Revision
98+ // Path limits the history to the commits that changed it. It must be the
99+ // same on every page of a listing.
100+ Path domain.TreePath
101+ // PageSize is 0 for the default, and lowered to MaxPageSize.
102+ PageSize int
103+ // PageToken is the NextPageToken of the previous page, or empty.
104+ PageToken string
105+}
106+
107+// HistoryPage is a page of history, newest first.
108+type HistoryPage struct {
109+ Commits []domain.Commit
110+ // NextPageToken is empty on the last page.
111+ NextPageToken string
112+}
113+
114+// WatchParams select the events of a watch.
115+type WatchParams struct {
116+ // Topics are TopicHome or "repo:<name>"; empty means TopicHome.
117+ Topics []string
118+ // ResumeAfter is the id of the last event the caller saw; empty to
119+ // receive only new events.
120+ ResumeAfter string
121+ // Client identifies where the watch comes from, for the limit per client
122+ // (MaxWatchersPerClient): an opaque key such as a normalized address; the
123+ // web passes its visitor's. Empty only for callers with no client, which
124+ // count against MaxWatchers alone.
125+ Client string
126+ // Ready, when not nil, is called once the topics are valid and the watch
127+ // is registered: events published from then on are not missed. Nothing is
128+ // sent before it returns.
129+ Ready func()
130+}
131+
132+// WatchEvent is one item of a watch.
133+type WatchEvent struct {
134+ // ID identifies the event, for resuming. Empty on a gap.
135+ ID string
136+ // Gap tells that events were lost: the caller must reload what it shows.
137+ // Event is nil then.
138+ Gap bool
139+ // Event is a domain.RepoCreated, RepoUpdated, RepoDeleted, PushEvent,
140+ // MergeRequestOpened, MergeRequestMerged, MergeRequestClosed or
141+ // CommentAdded.
142+ Event domain.Event
143+ // Repo is the state of the repository at delivery, for RepoCreated and
144+ // RepoUpdated.
145+ Repo *RepoView
146+}
147+
148+// CreateMergeRequestParams describes the merge request to open.
149+type CreateMergeRequestParams struct {
150+ Title string
151+ Description string
152+ // Source is required; Target defaults to the default branch.
153+ Source, Target string
154+}
155+
156+// ListMergeRequestsParams selects a page of merge requests.
157+type ListMergeRequestsParams struct {
158+ // State restricts the result; empty means every state.
159+ State domain.MRState
160+ // PageSize is 0 for the default, and lowered to MaxPageSize.
161+ PageSize int
162+ // PageToken is the NextPageToken of the previous page, or empty.
163+ PageToken string
164+}
165+
166+// MergeRequestPage is a page of merge requests, highest number first.
167+type MergeRequestPage struct {
168+ MergeRequests []domain.MergeRequest
169+ // NextPageToken is empty on the last page.
170+ NextPageToken string
171+}
172+
173+// MergeRequestDetail is a merge request with its review data.
174+type MergeRequestDetail struct {
175+ MergeRequest domain.MergeRequest
176+ // Commits are the commits of the head that the target lacks, newest
177+ // first, at most MaxMergeRequestCommits.
178+ Commits []domain.Commit
179+ CommitsTruncated bool
180+ // DiffBaseID is the commit the diff compares from; zero when the source
181+ // branch is gone, then the diff is empty.
182+ DiffBaseID domain.ObjectID
183+ Stats domain.DiffStats
184+ Diff domain.Diff
185+ // Comments are the comments, oldest first.
186+ Comments []domain.Comment
187+ // LastReadID is the head recorded by MarkRead; zero when none.
188+ LastReadID domain.ObjectID
189+ // LastReadLost tells that since-last-read was asked for, a head was
190+ // recorded, but that commit is gone: the diff is against the target.
191+ LastReadLost bool
192+}
193+
194+// AddCommentParams describes the comment to add.
195+type AddCommentParams struct {
196+ // Path, Line and Side designate a line of the diff; all empty for a
197+ // general comment.
198+ Path string
199+ Line int
200+ Side domain.DiffSide
201+ Text string
202+}
203+
204+// MergeParams describes the merge to perform.
205+type MergeParams struct {
206+ // Mode defaults to fast-forward.
207+ Mode domain.MergeMode
208+ // HeadID is the commit the caller reviewed; the source branch must still
209+ // point to it.
210+ HeadID domain.ObjectID
211+ // DeleteSource deletes the source branch after the merge.
212+ DeleteSource bool
213+}
214+
215+// MergeResult is a merged request and what happened to its source branch.
216+type MergeResult struct {
217+ domain.MergeRequest
218+ // SourceDeleted is true when DeleteSource was asked and the branch is
219+ // gone.
220+ SourceDeleted bool
221+}
222+
223+// LoginLink is a login link as the owner receives it.
224+type LoginLink struct {
225+ // URL holds the secret: show it once, never log it.
226+ URL string
227+ ExpiresAt time.Time
228+}
R internal/adapters/githttp/mocks/Authenticator.go → internal/ports/mocks/AuthService.go +18 -18 renamed, 52% similar
@@ -11,17 +11,17 @@ import (
1111 mock "github.com/stretchr/testify/mock"
1212 )
1313
14-// NewAuthenticator creates a new instance of Authenticator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
14+// NewAuthService creates a new instance of AuthService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
1515 // The first argument is typically a *testing.T value.
16-func NewAuthenticator(t interface {
16+func NewAuthService(t interface {
1717 mock.TestingT
1818 Cleanup(func())
19-}) *Authenticator {
19+}) *AuthService {
2020 if helper, ok := t.(interface{ Helper() }); ok {
2121 helper.Helper()
2222 }
2323
24- mock := &Authenticator{}
24+ mock := &AuthService{}
2525 mock.Mock.Test(t)
2626
2727 t.Cleanup(func() {
@@ -34,21 +34,21 @@ func NewAuthenticator(t interface {
3434 return mock
3535 }
3636
37-// Authenticator is an autogenerated mock type for the Authenticator type
38-type Authenticator struct {
37+// AuthService is an autogenerated mock type for the AuthService type
38+type AuthService struct {
3939 mock.Mock
4040 }
4141
42-type Authenticator_Expecter struct {
42+type AuthService_Expecter struct {
4343 mock *mock.Mock
4444 }
4545
46-func (_m *Authenticator) EXPECT() *Authenticator_Expecter {
47- return &Authenticator_Expecter{mock: &_m.Mock}
46+func (_m *AuthService) EXPECT() *AuthService_Expecter {
47+ return &AuthService_Expecter{mock: &_m.Mock}
4848 }
4949
50-// Authenticate provides a mock function for the type Authenticator
51-func (_mock *Authenticator) Authenticate(ctx context.Context, secret string) (domain.Principal, error) {
50+// Authenticate provides a mock function for the type AuthService
51+func (_mock *AuthService) Authenticate(ctx context.Context, secret string) (domain.Principal, error) {
5252 ret := _mock.Called(ctx, secret)
5353
5454 if len(ret) == 0 {
@@ -73,19 +73,19 @@ func (_mock *Authenticator) Authenticate(ctx context.Context, secret string) (do
7373 return r0, r1
7474 }
7575
76-// Authenticator_Authenticate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Authenticate'
77-type Authenticator_Authenticate_Call struct {
76+// AuthService_Authenticate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Authenticate'
77+type AuthService_Authenticate_Call struct {
7878 *mock.Call
7979 }
8080
8181 // Authenticate is a helper method to define mock.On call
8282 // - ctx context.Context
8383 // - secret string
84-func (_e *Authenticator_Expecter) Authenticate(ctx any, secret any) *Authenticator_Authenticate_Call {
85- return &Authenticator_Authenticate_Call{Call: _e.mock.On("Authenticate", ctx, secret)}
84+func (_e *AuthService_Expecter) Authenticate(ctx any, secret any) *AuthService_Authenticate_Call {
85+ return &AuthService_Authenticate_Call{Call: _e.mock.On("Authenticate", ctx, secret)}
8686 }
8787
88-func (_c *Authenticator_Authenticate_Call) Run(run func(ctx context.Context, secret string)) *Authenticator_Authenticate_Call {
88+func (_c *AuthService_Authenticate_Call) Run(run func(ctx context.Context, secret string)) *AuthService_Authenticate_Call {
8989 _c.Call.Run(func(args mock.Arguments) {
9090 var arg0 context.Context
9191 if args[0] != nil {
@@ -103,12 +103,12 @@ func (_c *Authenticator_Authenticate_Call) Run(run func(ctx context.Context, sec
103103 return _c
104104 }
105105
106-func (_c *Authenticator_Authenticate_Call) Return(principal domain.Principal, err error) *Authenticator_Authenticate_Call {
106+func (_c *AuthService_Authenticate_Call) Return(principal domain.Principal, err error) *AuthService_Authenticate_Call {
107107 _c.Call.Return(principal, err)
108108 return _c
109109 }
110110
111-func (_c *Authenticator_Authenticate_Call) RunAndReturn(run func(ctx context.Context, secret string) (domain.Principal, error)) *Authenticator_Authenticate_Call {
111+func (_c *AuthService_Authenticate_Call) RunAndReturn(run func(ctx context.Context, secret string) (domain.Principal, error)) *AuthService_Authenticate_Call {
112112 _c.Call.Return(run)
113113 return _c
114114 }
R internal/adapters/rpc/mocks/Browse.go → internal/ports/mocks/BrowseService.go +81 -81 renamed, 55% similar
@@ -8,22 +8,22 @@ import (
88 "context"
99 "io"
1010
11- "bnema.dev/ermite/internal/app"
1211 "bnema.dev/ermite/internal/domain"
12+ "bnema.dev/ermite/internal/ports"
1313 mock "github.com/stretchr/testify/mock"
1414 )
1515
16-// NewBrowse creates a new instance of Browse. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
16+// NewBrowseService creates a new instance of BrowseService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
1717 // The first argument is typically a *testing.T value.
18-func NewBrowse(t interface {
18+func NewBrowseService(t interface {
1919 mock.TestingT
2020 Cleanup(func())
21-}) *Browse {
21+}) *BrowseService {
2222 if helper, ok := t.(interface{ Helper() }); ok {
2323 helper.Helper()
2424 }
2525
26- mock := &Browse{}
26+ mock := &BrowseService{}
2727 mock.Mock.Test(t)
2828
2929 t.Cleanup(func() {
@@ -36,21 +36,21 @@ func NewBrowse(t interface {
3636 return mock
3737 }
3838
39-// Browse is an autogenerated mock type for the Browse type
40-type Browse struct {
39+// BrowseService is an autogenerated mock type for the BrowseService type
40+type BrowseService struct {
4141 mock.Mock
4242 }
4343
44-type Browse_Expecter struct {
44+type BrowseService_Expecter struct {
4545 mock *mock.Mock
4646 }
4747
48-func (_m *Browse) EXPECT() *Browse_Expecter {
49- return &Browse_Expecter{mock: &_m.Mock}
48+func (_m *BrowseService) EXPECT() *BrowseService_Expecter {
49+ return &BrowseService_Expecter{mock: &_m.Mock}
5050 }
5151
52-// GetCommit provides a mock function for the type Browse
53-func (_mock *Browse) GetCommit(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision) (domain.CommitDetail, error) {
52+// GetCommit provides a mock function for the type BrowseService
53+func (_mock *BrowseService) GetCommit(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision) (domain.CommitDetail, error) {
5454 ret := _mock.Called(ctx, p, repo, rev)
5555
5656 if len(ret) == 0 {
@@ -75,8 +75,8 @@ func (_mock *Browse) GetCommit(ctx context.Context, p domain.Principal, repo dom
7575 return r0, r1
7676 }
7777
78-// Browse_GetCommit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCommit'
79-type Browse_GetCommit_Call struct {
78+// BrowseService_GetCommit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCommit'
79+type BrowseService_GetCommit_Call struct {
8080 *mock.Call
8181 }
8282
@@ -85,11 +85,11 @@ type Browse_GetCommit_Call struct {
8585 // - p domain.Principal
8686 // - repo domain.RepoName
8787 // - rev domain.Revision
88-func (_e *Browse_Expecter) GetCommit(ctx any, p any, repo any, rev any) *Browse_GetCommit_Call {
89- return &Browse_GetCommit_Call{Call: _e.mock.On("GetCommit", ctx, p, repo, rev)}
88+func (_e *BrowseService_Expecter) GetCommit(ctx any, p any, repo any, rev any) *BrowseService_GetCommit_Call {
89+ return &BrowseService_GetCommit_Call{Call: _e.mock.On("GetCommit", ctx, p, repo, rev)}
9090 }
9191
92-func (_c *Browse_GetCommit_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision)) *Browse_GetCommit_Call {
92+func (_c *BrowseService_GetCommit_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision)) *BrowseService_GetCommit_Call {
9393 _c.Call.Run(func(args mock.Arguments) {
9494 var arg0 context.Context
9595 if args[0] != nil {
@@ -117,18 +117,18 @@ func (_c *Browse_GetCommit_Call) Run(run func(ctx context.Context, p domain.Prin
117117 return _c
118118 }
119119
120-func (_c *Browse_GetCommit_Call) Return(commitDetail domain.CommitDetail, err error) *Browse_GetCommit_Call {
120+func (_c *BrowseService_GetCommit_Call) Return(commitDetail domain.CommitDetail, err error) *BrowseService_GetCommit_Call {
121121 _c.Call.Return(commitDetail, err)
122122 return _c
123123 }
124124
125-func (_c *Browse_GetCommit_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision) (domain.CommitDetail, error)) *Browse_GetCommit_Call {
125+func (_c *BrowseService_GetCommit_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision) (domain.CommitDetail, error)) *BrowseService_GetCommit_Call {
126126 _c.Call.Return(run)
127127 return _c
128128 }
129129
130-// GetTree provides a mock function for the type Browse
131-func (_mock *Browse) GetTree(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision, path domain.TreePath, includeLastCommit bool) (domain.Tree, error) {
130+// GetTree provides a mock function for the type BrowseService
131+func (_mock *BrowseService) GetTree(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision, path domain.TreePath, includeLastCommit bool) (domain.Tree, error) {
132132 ret := _mock.Called(ctx, p, repo, rev, path, includeLastCommit)
133133
134134 if len(ret) == 0 {
@@ -153,8 +153,8 @@ func (_mock *Browse) GetTree(ctx context.Context, p domain.Principal, repo domai
153153 return r0, r1
154154 }
155155
156-// Browse_GetTree_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTree'
157-type Browse_GetTree_Call struct {
156+// BrowseService_GetTree_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTree'
157+type BrowseService_GetTree_Call struct {
158158 *mock.Call
159159 }
160160
@@ -165,11 +165,11 @@ type Browse_GetTree_Call struct {
165165 // - rev domain.Revision
166166 // - path domain.TreePath
167167 // - includeLastCommit bool
168-func (_e *Browse_Expecter) GetTree(ctx any, p any, repo any, rev any, path any, includeLastCommit any) *Browse_GetTree_Call {
169- return &Browse_GetTree_Call{Call: _e.mock.On("GetTree", ctx, p, repo, rev, path, includeLastCommit)}
168+func (_e *BrowseService_Expecter) GetTree(ctx any, p any, repo any, rev any, path any, includeLastCommit any) *BrowseService_GetTree_Call {
169+ return &BrowseService_GetTree_Call{Call: _e.mock.On("GetTree", ctx, p, repo, rev, path, includeLastCommit)}
170170 }
171171
172-func (_c *Browse_GetTree_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision, path domain.TreePath, includeLastCommit bool)) *Browse_GetTree_Call {
172+func (_c *BrowseService_GetTree_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision, path domain.TreePath, includeLastCommit bool)) *BrowseService_GetTree_Call {
173173 _c.Call.Run(func(args mock.Arguments) {
174174 var arg0 context.Context
175175 if args[0] != nil {
@@ -207,35 +207,35 @@ func (_c *Browse_GetTree_Call) Run(run func(ctx context.Context, p domain.Princi
207207 return _c
208208 }
209209
210-func (_c *Browse_GetTree_Call) Return(tree domain.Tree, err error) *Browse_GetTree_Call {
210+func (_c *BrowseService_GetTree_Call) Return(tree domain.Tree, err error) *BrowseService_GetTree_Call {
211211 _c.Call.Return(tree, err)
212212 return _c
213213 }
214214
215-func (_c *Browse_GetTree_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision, path domain.TreePath, includeLastCommit bool) (domain.Tree, error)) *Browse_GetTree_Call {
215+func (_c *BrowseService_GetTree_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision, path domain.TreePath, includeLastCommit bool) (domain.Tree, error)) *BrowseService_GetTree_Call {
216216 _c.Call.Return(run)
217217 return _c
218218 }
219219
220-// ListCommits provides a mock function for the type Browse
221-func (_mock *Browse) ListCommits(ctx context.Context, p domain.Principal, repo domain.RepoName, params app.ListCommitsParams) (app.CommitPage, error) {
220+// ListCommits provides a mock function for the type BrowseService
221+func (_mock *BrowseService) ListCommits(ctx context.Context, p domain.Principal, repo domain.RepoName, params ports.ListCommitsParams) (ports.HistoryPage, error) {
222222 ret := _mock.Called(ctx, p, repo, params)
223223
224224 if len(ret) == 0 {
225225 panic("no return value specified for ListCommits")
226226 }
227227
228- var r0 app.CommitPage
228+ var r0 ports.HistoryPage
229229 var r1 error
230- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, app.ListCommitsParams) (app.CommitPage, error)); ok {
230+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, ports.ListCommitsParams) (ports.HistoryPage, error)); ok {
231231 return returnFunc(ctx, p, repo, params)
232232 }
233- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, app.ListCommitsParams) app.CommitPage); ok {
233+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, ports.ListCommitsParams) ports.HistoryPage); ok {
234234 r0 = returnFunc(ctx, p, repo, params)
235235 } else {
236- r0 = ret.Get(0).(app.CommitPage)
236+ r0 = ret.Get(0).(ports.HistoryPage)
237237 }
238- if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, app.ListCommitsParams) error); ok {
238+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, ports.ListCommitsParams) error); ok {
239239 r1 = returnFunc(ctx, p, repo, params)
240240 } else {
241241 r1 = ret.Error(1)
@@ -243,8 +243,8 @@ func (_mock *Browse) ListCommits(ctx context.Context, p domain.Principal, repo d
243243 return r0, r1
244244 }
245245
246-// Browse_ListCommits_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListCommits'
247-type Browse_ListCommits_Call struct {
246+// BrowseService_ListCommits_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListCommits'
247+type BrowseService_ListCommits_Call struct {
248248 *mock.Call
249249 }
250250
@@ -252,12 +252,12 @@ type Browse_ListCommits_Call struct {
252252 // - ctx context.Context
253253 // - p domain.Principal
254254 // - repo domain.RepoName
255-// - params app.ListCommitsParams
256-func (_e *Browse_Expecter) ListCommits(ctx any, p any, repo any, params any) *Browse_ListCommits_Call {
257- return &Browse_ListCommits_Call{Call: _e.mock.On("ListCommits", ctx, p, repo, params)}
255+// - params ports.ListCommitsParams
256+func (_e *BrowseService_Expecter) ListCommits(ctx any, p any, repo any, params any) *BrowseService_ListCommits_Call {
257+ return &BrowseService_ListCommits_Call{Call: _e.mock.On("ListCommits", ctx, p, repo, params)}
258258 }
259259
260-func (_c *Browse_ListCommits_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, params app.ListCommitsParams)) *Browse_ListCommits_Call {
260+func (_c *BrowseService_ListCommits_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, params ports.ListCommitsParams)) *BrowseService_ListCommits_Call {
261261 _c.Call.Run(func(args mock.Arguments) {
262262 var arg0 context.Context
263263 if args[0] != nil {
@@ -271,9 +271,9 @@ func (_c *Browse_ListCommits_Call) Run(run func(ctx context.Context, p domain.Pr
271271 if args[2] != nil {
272272 arg2 = args[2].(domain.RepoName)
273273 }
274- var arg3 app.ListCommitsParams
274+ var arg3 ports.ListCommitsParams
275275 if args[3] != nil {
276- arg3 = args[3].(app.ListCommitsParams)
276+ arg3 = args[3].(ports.ListCommitsParams)
277277 }
278278 run(
279279 arg0,
@@ -285,35 +285,35 @@ func (_c *Browse_ListCommits_Call) Run(run func(ctx context.Context, p domain.Pr
285285 return _c
286286 }
287287
288-func (_c *Browse_ListCommits_Call) Return(commitPage app.CommitPage, err error) *Browse_ListCommits_Call {
289- _c.Call.Return(commitPage, err)
288+func (_c *BrowseService_ListCommits_Call) Return(historyPage ports.HistoryPage, err error) *BrowseService_ListCommits_Call {
289+ _c.Call.Return(historyPage, err)
290290 return _c
291291 }
292292
293-func (_c *Browse_ListCommits_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, params app.ListCommitsParams) (app.CommitPage, error)) *Browse_ListCommits_Call {
293+func (_c *BrowseService_ListCommits_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, params ports.ListCommitsParams) (ports.HistoryPage, error)) *BrowseService_ListCommits_Call {
294294 _c.Call.Return(run)
295295 return _c
296296 }
297297
298-// ListRefs provides a mock function for the type Browse
299-func (_mock *Browse) ListRefs(ctx context.Context, p domain.Principal, repo domain.RepoName, params app.ListRefsParams) (app.RefPage, error) {
298+// ListRefs provides a mock function for the type BrowseService
299+func (_mock *BrowseService) ListRefs(ctx context.Context, p domain.Principal, repo domain.RepoName, params ports.ListRefsParams) (ports.RefPage, error) {
300300 ret := _mock.Called(ctx, p, repo, params)
301301
302302 if len(ret) == 0 {
303303 panic("no return value specified for ListRefs")
304304 }
305305
306- var r0 app.RefPage
306+ var r0 ports.RefPage
307307 var r1 error
308- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, app.ListRefsParams) (app.RefPage, error)); ok {
308+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, ports.ListRefsParams) (ports.RefPage, error)); ok {
309309 return returnFunc(ctx, p, repo, params)
310310 }
311- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, app.ListRefsParams) app.RefPage); ok {
311+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, ports.ListRefsParams) ports.RefPage); ok {
312312 r0 = returnFunc(ctx, p, repo, params)
313313 } else {
314- r0 = ret.Get(0).(app.RefPage)
314+ r0 = ret.Get(0).(ports.RefPage)
315315 }
316- if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, app.ListRefsParams) error); ok {
316+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, ports.ListRefsParams) error); ok {
317317 r1 = returnFunc(ctx, p, repo, params)
318318 } else {
319319 r1 = ret.Error(1)
@@ -321,8 +321,8 @@ func (_mock *Browse) ListRefs(ctx context.Context, p domain.Principal, repo doma
321321 return r0, r1
322322 }
323323
324-// Browse_ListRefs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListRefs'
325-type Browse_ListRefs_Call struct {
324+// BrowseService_ListRefs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListRefs'
325+type BrowseService_ListRefs_Call struct {
326326 *mock.Call
327327 }
328328
@@ -330,12 +330,12 @@ type Browse_ListRefs_Call struct {
330330 // - ctx context.Context
331331 // - p domain.Principal
332332 // - repo domain.RepoName
333-// - params app.ListRefsParams
334-func (_e *Browse_Expecter) ListRefs(ctx any, p any, repo any, params any) *Browse_ListRefs_Call {
335- return &Browse_ListRefs_Call{Call: _e.mock.On("ListRefs", ctx, p, repo, params)}
333+// - params ports.ListRefsParams
334+func (_e *BrowseService_Expecter) ListRefs(ctx any, p any, repo any, params any) *BrowseService_ListRefs_Call {
335+ return &BrowseService_ListRefs_Call{Call: _e.mock.On("ListRefs", ctx, p, repo, params)}
336336 }
337337
338-func (_c *Browse_ListRefs_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, params app.ListRefsParams)) *Browse_ListRefs_Call {
338+func (_c *BrowseService_ListRefs_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, params ports.ListRefsParams)) *BrowseService_ListRefs_Call {
339339 _c.Call.Run(func(args mock.Arguments) {
340340 var arg0 context.Context
341341 if args[0] != nil {
@@ -349,9 +349,9 @@ func (_c *Browse_ListRefs_Call) Run(run func(ctx context.Context, p domain.Princ
349349 if args[2] != nil {
350350 arg2 = args[2].(domain.RepoName)
351351 }
352- var arg3 app.ListRefsParams
352+ var arg3 ports.ListRefsParams
353353 if args[3] != nil {
354- arg3 = args[3].(app.ListRefsParams)
354+ arg3 = args[3].(ports.ListRefsParams)
355355 }
356356 run(
357357 arg0,
@@ -363,18 +363,18 @@ func (_c *Browse_ListRefs_Call) Run(run func(ctx context.Context, p domain.Princ
363363 return _c
364364 }
365365
366-func (_c *Browse_ListRefs_Call) Return(refPage app.RefPage, err error) *Browse_ListRefs_Call {
366+func (_c *BrowseService_ListRefs_Call) Return(refPage ports.RefPage, err error) *BrowseService_ListRefs_Call {
367367 _c.Call.Return(refPage, err)
368368 return _c
369369 }
370370
371-func (_c *Browse_ListRefs_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, params app.ListRefsParams) (app.RefPage, error)) *Browse_ListRefs_Call {
371+func (_c *BrowseService_ListRefs_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, params ports.ListRefsParams) (ports.RefPage, error)) *BrowseService_ListRefs_Call {
372372 _c.Call.Return(run)
373373 return _c
374374 }
375375
376-// OpenBlob provides a mock function for the type Browse
377-func (_mock *Browse) OpenBlob(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision, path domain.TreePath) (domain.BlobInfo, io.ReadCloser, error) {
376+// OpenBlob provides a mock function for the type BrowseService
377+func (_mock *BrowseService) OpenBlob(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision, path domain.TreePath) (domain.BlobInfo, io.ReadCloser, error) {
378378 ret := _mock.Called(ctx, p, repo, rev, path)
379379
380380 if len(ret) == 0 {
@@ -407,8 +407,8 @@ func (_mock *Browse) OpenBlob(ctx context.Context, p domain.Principal, repo doma
407407 return r0, r1, r2
408408 }
409409
410-// Browse_OpenBlob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OpenBlob'
411-type Browse_OpenBlob_Call struct {
410+// BrowseService_OpenBlob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OpenBlob'
411+type BrowseService_OpenBlob_Call struct {
412412 *mock.Call
413413 }
414414
@@ -418,11 +418,11 @@ type Browse_OpenBlob_Call struct {
418418 // - repo domain.RepoName
419419 // - rev domain.Revision
420420 // - path domain.TreePath
421-func (_e *Browse_Expecter) OpenBlob(ctx any, p any, repo any, rev any, path any) *Browse_OpenBlob_Call {
422- return &Browse_OpenBlob_Call{Call: _e.mock.On("OpenBlob", ctx, p, repo, rev, path)}
421+func (_e *BrowseService_Expecter) OpenBlob(ctx any, p any, repo any, rev any, path any) *BrowseService_OpenBlob_Call {
422+ return &BrowseService_OpenBlob_Call{Call: _e.mock.On("OpenBlob", ctx, p, repo, rev, path)}
423423 }
424424
425-func (_c *Browse_OpenBlob_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision, path domain.TreePath)) *Browse_OpenBlob_Call {
425+func (_c *BrowseService_OpenBlob_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision, path domain.TreePath)) *BrowseService_OpenBlob_Call {
426426 _c.Call.Run(func(args mock.Arguments) {
427427 var arg0 context.Context
428428 if args[0] != nil {
@@ -455,18 +455,18 @@ func (_c *Browse_OpenBlob_Call) Run(run func(ctx context.Context, p domain.Princ
455455 return _c
456456 }
457457
458-func (_c *Browse_OpenBlob_Call) Return(blobInfo domain.BlobInfo, readCloser io.ReadCloser, err error) *Browse_OpenBlob_Call {
458+func (_c *BrowseService_OpenBlob_Call) Return(blobInfo domain.BlobInfo, readCloser io.ReadCloser, err error) *BrowseService_OpenBlob_Call {
459459 _c.Call.Return(blobInfo, readCloser, err)
460460 return _c
461461 }
462462
463-func (_c *Browse_OpenBlob_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision, path domain.TreePath) (domain.BlobInfo, io.ReadCloser, error)) *Browse_OpenBlob_Call {
463+func (_c *BrowseService_OpenBlob_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision, path domain.TreePath) (domain.BlobInfo, io.ReadCloser, error)) *BrowseService_OpenBlob_Call {
464464 _c.Call.Return(run)
465465 return _c
466466 }
467467
468-// ResolveRevision provides a mock function for the type Browse
469-func (_mock *Browse) ResolveRevision(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision) (domain.ResolvedRevision, error) {
468+// ResolveRevision provides a mock function for the type BrowseService
469+func (_mock *BrowseService) ResolveRevision(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision) (domain.ResolvedRevision, error) {
470470 ret := _mock.Called(ctx, p, repo, rev)
471471
472472 if len(ret) == 0 {
@@ -491,8 +491,8 @@ func (_mock *Browse) ResolveRevision(ctx context.Context, p domain.Principal, re
491491 return r0, r1
492492 }
493493
494-// Browse_ResolveRevision_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ResolveRevision'
495-type Browse_ResolveRevision_Call struct {
494+// BrowseService_ResolveRevision_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ResolveRevision'
495+type BrowseService_ResolveRevision_Call struct {
496496 *mock.Call
497497 }
498498
@@ -501,11 +501,11 @@ type Browse_ResolveRevision_Call struct {
501501 // - p domain.Principal
502502 // - repo domain.RepoName
503503 // - rev domain.Revision
504-func (_e *Browse_Expecter) ResolveRevision(ctx any, p any, repo any, rev any) *Browse_ResolveRevision_Call {
505- return &Browse_ResolveRevision_Call{Call: _e.mock.On("ResolveRevision", ctx, p, repo, rev)}
504+func (_e *BrowseService_Expecter) ResolveRevision(ctx any, p any, repo any, rev any) *BrowseService_ResolveRevision_Call {
505+ return &BrowseService_ResolveRevision_Call{Call: _e.mock.On("ResolveRevision", ctx, p, repo, rev)}
506506 }
507507
508-func (_c *Browse_ResolveRevision_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision)) *Browse_ResolveRevision_Call {
508+func (_c *BrowseService_ResolveRevision_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision)) *BrowseService_ResolveRevision_Call {
509509 _c.Call.Run(func(args mock.Arguments) {
510510 var arg0 context.Context
511511 if args[0] != nil {
@@ -533,12 +533,12 @@ func (_c *Browse_ResolveRevision_Call) Run(run func(ctx context.Context, p domai
533533 return _c
534534 }
535535
536-func (_c *Browse_ResolveRevision_Call) Return(resolvedRevision domain.ResolvedRevision, err error) *Browse_ResolveRevision_Call {
536+func (_c *BrowseService_ResolveRevision_Call) Return(resolvedRevision domain.ResolvedRevision, err error) *BrowseService_ResolveRevision_Call {
537537 _c.Call.Return(resolvedRevision, err)
538538 return _c
539539 }
540540
541-func (_c *Browse_ResolveRevision_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision) (domain.ResolvedRevision, error)) *Browse_ResolveRevision_Call {
541+func (_c *BrowseService_ResolveRevision_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision) (domain.ResolvedRevision, error)) *BrowseService_ResolveRevision_Call {
542542 _c.Call.Return(run)
543543 return _c
544544 }
A internal/ports/mocks/EventService.go +118 -0
@@ -0,0 +1,118 @@
1+// Code generated by mockery; DO NOT EDIT.
2+// github.com/vektra/mockery
3+// template: testify
4+
5+package mocks
6+
7+import (
8+ "context"
9+
10+ "bnema.dev/ermite/internal/domain"
11+ "bnema.dev/ermite/internal/ports"
12+ mock "github.com/stretchr/testify/mock"
13+)
14+
15+// NewEventService creates a new instance of EventService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
16+// The first argument is typically a *testing.T value.
17+func NewEventService(t interface {
18+ mock.TestingT
19+ Cleanup(func())
20+}) *EventService {
21+ if helper, ok := t.(interface{ Helper() }); ok {
22+ helper.Helper()
23+ }
24+
25+ mock := &EventService{}
26+ mock.Mock.Test(t)
27+
28+ t.Cleanup(func() {
29+ if helper, ok := t.(interface{ Helper() }); ok {
30+ helper.Helper()
31+ }
32+ mock.AssertExpectations(t)
33+ })
34+
35+ return mock
36+}
37+
38+// EventService is an autogenerated mock type for the EventService type
39+type EventService struct {
40+ mock.Mock
41+}
42+
43+type EventService_Expecter struct {
44+ mock *mock.Mock
45+}
46+
47+func (_m *EventService) EXPECT() *EventService_Expecter {
48+ return &EventService_Expecter{mock: &_m.Mock}
49+}
50+
51+// Watch provides a mock function for the type EventService
52+func (_mock *EventService) Watch(ctx context.Context, p domain.Principal, params ports.WatchParams, send func(ports.WatchEvent) error) error {
53+ ret := _mock.Called(ctx, p, params, send)
54+
55+ if len(ret) == 0 {
56+ panic("no return value specified for Watch")
57+ }
58+
59+ var r0 error
60+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, ports.WatchParams, func(ports.WatchEvent) error) error); ok {
61+ r0 = returnFunc(ctx, p, params, send)
62+ } else {
63+ r0 = ret.Error(0)
64+ }
65+ return r0
66+}
67+
68+// EventService_Watch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Watch'
69+type EventService_Watch_Call struct {
70+ *mock.Call
71+}
72+
73+// Watch is a helper method to define mock.On call
74+// - ctx context.Context
75+// - p domain.Principal
76+// - params ports.WatchParams
77+// - send func(ports.WatchEvent) error
78+func (_e *EventService_Expecter) Watch(ctx any, p any, params any, send any) *EventService_Watch_Call {
79+ return &EventService_Watch_Call{Call: _e.mock.On("Watch", ctx, p, params, send)}
80+}
81+
82+func (_c *EventService_Watch_Call) Run(run func(ctx context.Context, p domain.Principal, params ports.WatchParams, send func(ports.WatchEvent) error)) *EventService_Watch_Call {
83+ _c.Call.Run(func(args mock.Arguments) {
84+ var arg0 context.Context
85+ if args[0] != nil {
86+ arg0 = args[0].(context.Context)
87+ }
88+ var arg1 domain.Principal
89+ if args[1] != nil {
90+ arg1 = args[1].(domain.Principal)
91+ }
92+ var arg2 ports.WatchParams
93+ if args[2] != nil {
94+ arg2 = args[2].(ports.WatchParams)
95+ }
96+ var arg3 func(ports.WatchEvent) error
97+ if args[3] != nil {
98+ arg3 = args[3].(func(ports.WatchEvent) error)
99+ }
100+ run(
101+ arg0,
102+ arg1,
103+ arg2,
104+ arg3,
105+ )
106+ })
107+ return _c
108+}
109+
110+func (_c *EventService_Watch_Call) Return(err error) *EventService_Watch_Call {
111+ _c.Call.Return(err)
112+ return _c
113+}
114+
115+func (_c *EventService_Watch_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, params ports.WatchParams, send func(ports.WatchEvent) error) error) *EventService_Watch_Call {
116+ _c.Call.Return(run)
117+ return _c
118+}
R internal/adapters/githttp/mocks/FailureLimiter.go → internal/ports/mocks/FailureLimiter.go +0 -0 renamed, 100% similar

renamed, 100% similar

R internal/adapters/githttp/mocks/HookTokens.go → internal/ports/mocks/HookTokens.go +0 -0 renamed, 100% similar

renamed, 100% similar

R internal/app/mocks/MaintenanceScheduler.go → internal/ports/mocks/MaintenanceScheduler.go +0 -0 renamed, 100% similar

renamed, 100% similar

R internal/adapters/rpc/mocks/MergeRequests.go → internal/ports/mocks/MergeRequestService.go +114 -114 renamed, 50% similar
@@ -7,22 +7,22 @@ package mocks
77 import (
88 "context"
99
10- "bnema.dev/ermite/internal/app"
1110 "bnema.dev/ermite/internal/domain"
11+ "bnema.dev/ermite/internal/ports"
1212 mock "github.com/stretchr/testify/mock"
1313 )
1414
15-// NewMergeRequests creates a new instance of MergeRequests. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
15+// NewMergeRequestService creates a new instance of MergeRequestService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
1616 // The first argument is typically a *testing.T value.
17-func NewMergeRequests(t interface {
17+func NewMergeRequestService(t interface {
1818 mock.TestingT
1919 Cleanup(func())
20-}) *MergeRequests {
20+}) *MergeRequestService {
2121 if helper, ok := t.(interface{ Helper() }); ok {
2222 helper.Helper()
2323 }
2424
25- mock := &MergeRequests{}
25+ mock := &MergeRequestService{}
2626 mock.Mock.Test(t)
2727
2828 t.Cleanup(func() {
@@ -35,21 +35,21 @@ func NewMergeRequests(t interface {
3535 return mock
3636 }
3737
38-// MergeRequests is an autogenerated mock type for the MergeRequests type
39-type MergeRequests struct {
38+// MergeRequestService is an autogenerated mock type for the MergeRequestService type
39+type MergeRequestService struct {
4040 mock.Mock
4141 }
4242
43-type MergeRequests_Expecter struct {
43+type MergeRequestService_Expecter struct {
4444 mock *mock.Mock
4545 }
4646
47-func (_m *MergeRequests) EXPECT() *MergeRequests_Expecter {
48- return &MergeRequests_Expecter{mock: &_m.Mock}
47+func (_m *MergeRequestService) EXPECT() *MergeRequestService_Expecter {
48+ return &MergeRequestService_Expecter{mock: &_m.Mock}
4949 }
5050
51-// AddComment provides a mock function for the type MergeRequests
52-func (_mock *MergeRequests) AddComment(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params app.AddCommentParams) (domain.Comment, error) {
51+// AddComment provides a mock function for the type MergeRequestService
52+func (_mock *MergeRequestService) AddComment(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params ports.AddCommentParams) (domain.Comment, error) {
5353 ret := _mock.Called(ctx, p, name, number, params)
5454
5555 if len(ret) == 0 {
@@ -58,15 +58,15 @@ func (_mock *MergeRequests) AddComment(ctx context.Context, p domain.Principal,
5858
5959 var r0 domain.Comment
6060 var r1 error
61- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64, app.AddCommentParams) (domain.Comment, error)); ok {
61+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64, ports.AddCommentParams) (domain.Comment, error)); ok {
6262 return returnFunc(ctx, p, name, number, params)
6363 }
64- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64, app.AddCommentParams) domain.Comment); ok {
64+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64, ports.AddCommentParams) domain.Comment); ok {
6565 r0 = returnFunc(ctx, p, name, number, params)
6666 } else {
6767 r0 = ret.Get(0).(domain.Comment)
6868 }
69- if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, int64, app.AddCommentParams) error); ok {
69+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, int64, ports.AddCommentParams) error); ok {
7070 r1 = returnFunc(ctx, p, name, number, params)
7171 } else {
7272 r1 = ret.Error(1)
@@ -74,8 +74,8 @@ func (_mock *MergeRequests) AddComment(ctx context.Context, p domain.Principal,
7474 return r0, r1
7575 }
7676
77-// MergeRequests_AddComment_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddComment'
78-type MergeRequests_AddComment_Call struct {
77+// MergeRequestService_AddComment_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddComment'
78+type MergeRequestService_AddComment_Call struct {
7979 *mock.Call
8080 }
8181
@@ -84,12 +84,12 @@ type MergeRequests_AddComment_Call struct {
8484 // - p domain.Principal
8585 // - name domain.RepoName
8686 // - number int64
87-// - params app.AddCommentParams
88-func (_e *MergeRequests_Expecter) AddComment(ctx any, p any, name any, number any, params any) *MergeRequests_AddComment_Call {
89- return &MergeRequests_AddComment_Call{Call: _e.mock.On("AddComment", ctx, p, name, number, params)}
87+// - params ports.AddCommentParams
88+func (_e *MergeRequestService_Expecter) AddComment(ctx any, p any, name any, number any, params any) *MergeRequestService_AddComment_Call {
89+ return &MergeRequestService_AddComment_Call{Call: _e.mock.On("AddComment", ctx, p, name, number, params)}
9090 }
9191
92-func (_c *MergeRequests_AddComment_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params app.AddCommentParams)) *MergeRequests_AddComment_Call {
92+func (_c *MergeRequestService_AddComment_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params ports.AddCommentParams)) *MergeRequestService_AddComment_Call {
9393 _c.Call.Run(func(args mock.Arguments) {
9494 var arg0 context.Context
9595 if args[0] != nil {
@@ -107,9 +107,9 @@ func (_c *MergeRequests_AddComment_Call) Run(run func(ctx context.Context, p dom
107107 if args[3] != nil {
108108 arg3 = args[3].(int64)
109109 }
110- var arg4 app.AddCommentParams
110+ var arg4 ports.AddCommentParams
111111 if args[4] != nil {
112- arg4 = args[4].(app.AddCommentParams)
112+ arg4 = args[4].(ports.AddCommentParams)
113113 }
114114 run(
115115 arg0,
@@ -122,18 +122,18 @@ func (_c *MergeRequests_AddComment_Call) Run(run func(ctx context.Context, p dom
122122 return _c
123123 }
124124
125-func (_c *MergeRequests_AddComment_Call) Return(comment domain.Comment, err error) *MergeRequests_AddComment_Call {
125+func (_c *MergeRequestService_AddComment_Call) Return(comment domain.Comment, err error) *MergeRequestService_AddComment_Call {
126126 _c.Call.Return(comment, err)
127127 return _c
128128 }
129129
130-func (_c *MergeRequests_AddComment_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params app.AddCommentParams) (domain.Comment, error)) *MergeRequests_AddComment_Call {
130+func (_c *MergeRequestService_AddComment_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params ports.AddCommentParams) (domain.Comment, error)) *MergeRequestService_AddComment_Call {
131131 _c.Call.Return(run)
132132 return _c
133133 }
134134
135-// Close provides a mock function for the type MergeRequests
136-func (_mock *MergeRequests) Close(ctx context.Context, p domain.Principal, name domain.RepoName, number int64) (domain.MergeRequest, error) {
135+// Close provides a mock function for the type MergeRequestService
136+func (_mock *MergeRequestService) Close(ctx context.Context, p domain.Principal, name domain.RepoName, number int64) (domain.MergeRequest, error) {
137137 ret := _mock.Called(ctx, p, name, number)
138138
139139 if len(ret) == 0 {
@@ -158,8 +158,8 @@ func (_mock *MergeRequests) Close(ctx context.Context, p domain.Principal, name
158158 return r0, r1
159159 }
160160
161-// MergeRequests_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close'
162-type MergeRequests_Close_Call struct {
161+// MergeRequestService_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close'
162+type MergeRequestService_Close_Call struct {
163163 *mock.Call
164164 }
165165
@@ -168,11 +168,11 @@ type MergeRequests_Close_Call struct {
168168 // - p domain.Principal
169169 // - name domain.RepoName
170170 // - number int64
171-func (_e *MergeRequests_Expecter) Close(ctx any, p any, name any, number any) *MergeRequests_Close_Call {
172- return &MergeRequests_Close_Call{Call: _e.mock.On("Close", ctx, p, name, number)}
171+func (_e *MergeRequestService_Expecter) Close(ctx any, p any, name any, number any) *MergeRequestService_Close_Call {
172+ return &MergeRequestService_Close_Call{Call: _e.mock.On("Close", ctx, p, name, number)}
173173 }
174174
175-func (_c *MergeRequests_Close_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64)) *MergeRequests_Close_Call {
175+func (_c *MergeRequestService_Close_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64)) *MergeRequestService_Close_Call {
176176 _c.Call.Run(func(args mock.Arguments) {
177177 var arg0 context.Context
178178 if args[0] != nil {
@@ -200,18 +200,18 @@ func (_c *MergeRequests_Close_Call) Run(run func(ctx context.Context, p domain.P
200200 return _c
201201 }
202202
203-func (_c *MergeRequests_Close_Call) Return(mergeRequest domain.MergeRequest, err error) *MergeRequests_Close_Call {
203+func (_c *MergeRequestService_Close_Call) Return(mergeRequest domain.MergeRequest, err error) *MergeRequestService_Close_Call {
204204 _c.Call.Return(mergeRequest, err)
205205 return _c
206206 }
207207
208-func (_c *MergeRequests_Close_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64) (domain.MergeRequest, error)) *MergeRequests_Close_Call {
208+func (_c *MergeRequestService_Close_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64) (domain.MergeRequest, error)) *MergeRequestService_Close_Call {
209209 _c.Call.Return(run)
210210 return _c
211211 }
212212
213-// Create provides a mock function for the type MergeRequests
214-func (_mock *MergeRequests) Create(ctx context.Context, p domain.Principal, name domain.RepoName, params app.CreateMergeRequestParams) (domain.MergeRequest, error) {
213+// Create provides a mock function for the type MergeRequestService
214+func (_mock *MergeRequestService) Create(ctx context.Context, p domain.Principal, name domain.RepoName, params ports.CreateMergeRequestParams) (domain.MergeRequest, error) {
215215 ret := _mock.Called(ctx, p, name, params)
216216
217217 if len(ret) == 0 {
@@ -220,15 +220,15 @@ func (_mock *MergeRequests) Create(ctx context.Context, p domain.Principal, name
220220
221221 var r0 domain.MergeRequest
222222 var r1 error
223- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, app.CreateMergeRequestParams) (domain.MergeRequest, error)); ok {
223+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, ports.CreateMergeRequestParams) (domain.MergeRequest, error)); ok {
224224 return returnFunc(ctx, p, name, params)
225225 }
226- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, app.CreateMergeRequestParams) domain.MergeRequest); ok {
226+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, ports.CreateMergeRequestParams) domain.MergeRequest); ok {
227227 r0 = returnFunc(ctx, p, name, params)
228228 } else {
229229 r0 = ret.Get(0).(domain.MergeRequest)
230230 }
231- if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, app.CreateMergeRequestParams) error); ok {
231+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, ports.CreateMergeRequestParams) error); ok {
232232 r1 = returnFunc(ctx, p, name, params)
233233 } else {
234234 r1 = ret.Error(1)
@@ -236,8 +236,8 @@ func (_mock *MergeRequests) Create(ctx context.Context, p domain.Principal, name
236236 return r0, r1
237237 }
238238
239-// MergeRequests_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create'
240-type MergeRequests_Create_Call struct {
239+// MergeRequestService_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create'
240+type MergeRequestService_Create_Call struct {
241241 *mock.Call
242242 }
243243
@@ -245,12 +245,12 @@ type MergeRequests_Create_Call struct {
245245 // - ctx context.Context
246246 // - p domain.Principal
247247 // - name domain.RepoName
248-// - params app.CreateMergeRequestParams
249-func (_e *MergeRequests_Expecter) Create(ctx any, p any, name any, params any) *MergeRequests_Create_Call {
250- return &MergeRequests_Create_Call{Call: _e.mock.On("Create", ctx, p, name, params)}
248+// - params ports.CreateMergeRequestParams
249+func (_e *MergeRequestService_Expecter) Create(ctx any, p any, name any, params any) *MergeRequestService_Create_Call {
250+ return &MergeRequestService_Create_Call{Call: _e.mock.On("Create", ctx, p, name, params)}
251251 }
252252
253-func (_c *MergeRequests_Create_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, params app.CreateMergeRequestParams)) *MergeRequests_Create_Call {
253+func (_c *MergeRequestService_Create_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, params ports.CreateMergeRequestParams)) *MergeRequestService_Create_Call {
254254 _c.Call.Run(func(args mock.Arguments) {
255255 var arg0 context.Context
256256 if args[0] != nil {
@@ -264,9 +264,9 @@ func (_c *MergeRequests_Create_Call) Run(run func(ctx context.Context, p domain.
264264 if args[2] != nil {
265265 arg2 = args[2].(domain.RepoName)
266266 }
267- var arg3 app.CreateMergeRequestParams
267+ var arg3 ports.CreateMergeRequestParams
268268 if args[3] != nil {
269- arg3 = args[3].(app.CreateMergeRequestParams)
269+ arg3 = args[3].(ports.CreateMergeRequestParams)
270270 }
271271 run(
272272 arg0,
@@ -278,33 +278,33 @@ func (_c *MergeRequests_Create_Call) Run(run func(ctx context.Context, p domain.
278278 return _c
279279 }
280280
281-func (_c *MergeRequests_Create_Call) Return(mergeRequest domain.MergeRequest, err error) *MergeRequests_Create_Call {
281+func (_c *MergeRequestService_Create_Call) Return(mergeRequest domain.MergeRequest, err error) *MergeRequestService_Create_Call {
282282 _c.Call.Return(mergeRequest, err)
283283 return _c
284284 }
285285
286-func (_c *MergeRequests_Create_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, params app.CreateMergeRequestParams) (domain.MergeRequest, error)) *MergeRequests_Create_Call {
286+func (_c *MergeRequestService_Create_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, params ports.CreateMergeRequestParams) (domain.MergeRequest, error)) *MergeRequestService_Create_Call {
287287 _c.Call.Return(run)
288288 return _c
289289 }
290290
291-// Get provides a mock function for the type MergeRequests
292-func (_mock *MergeRequests) Get(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, sinceLastRead bool) (app.MergeRequestDetail, error) {
291+// Get provides a mock function for the type MergeRequestService
292+func (_mock *MergeRequestService) Get(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, sinceLastRead bool) (ports.MergeRequestDetail, error) {
293293 ret := _mock.Called(ctx, p, name, number, sinceLastRead)
294294
295295 if len(ret) == 0 {
296296 panic("no return value specified for Get")
297297 }
298298
299- var r0 app.MergeRequestDetail
299+ var r0 ports.MergeRequestDetail
300300 var r1 error
301- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64, bool) (app.MergeRequestDetail, error)); ok {
301+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64, bool) (ports.MergeRequestDetail, error)); ok {
302302 return returnFunc(ctx, p, name, number, sinceLastRead)
303303 }
304- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64, bool) app.MergeRequestDetail); ok {
304+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64, bool) ports.MergeRequestDetail); ok {
305305 r0 = returnFunc(ctx, p, name, number, sinceLastRead)
306306 } else {
307- r0 = ret.Get(0).(app.MergeRequestDetail)
307+ r0 = ret.Get(0).(ports.MergeRequestDetail)
308308 }
309309 if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, int64, bool) error); ok {
310310 r1 = returnFunc(ctx, p, name, number, sinceLastRead)
@@ -314,8 +314,8 @@ func (_mock *MergeRequests) Get(ctx context.Context, p domain.Principal, name do
314314 return r0, r1
315315 }
316316
317-// MergeRequests_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get'
318-type MergeRequests_Get_Call struct {
317+// MergeRequestService_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get'
318+type MergeRequestService_Get_Call struct {
319319 *mock.Call
320320 }
321321
@@ -325,11 +325,11 @@ type MergeRequests_Get_Call struct {
325325 // - name domain.RepoName
326326 // - number int64
327327 // - sinceLastRead bool
328-func (_e *MergeRequests_Expecter) Get(ctx any, p any, name any, number any, sinceLastRead any) *MergeRequests_Get_Call {
329- return &MergeRequests_Get_Call{Call: _e.mock.On("Get", ctx, p, name, number, sinceLastRead)}
328+func (_e *MergeRequestService_Expecter) Get(ctx any, p any, name any, number any, sinceLastRead any) *MergeRequestService_Get_Call {
329+ return &MergeRequestService_Get_Call{Call: _e.mock.On("Get", ctx, p, name, number, sinceLastRead)}
330330 }
331331
332-func (_c *MergeRequests_Get_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, sinceLastRead bool)) *MergeRequests_Get_Call {
332+func (_c *MergeRequestService_Get_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, sinceLastRead bool)) *MergeRequestService_Get_Call {
333333 _c.Call.Run(func(args mock.Arguments) {
334334 var arg0 context.Context
335335 if args[0] != nil {
@@ -362,35 +362,35 @@ func (_c *MergeRequests_Get_Call) Run(run func(ctx context.Context, p domain.Pri
362362 return _c
363363 }
364364
365-func (_c *MergeRequests_Get_Call) Return(mergeRequestDetail app.MergeRequestDetail, err error) *MergeRequests_Get_Call {
365+func (_c *MergeRequestService_Get_Call) Return(mergeRequestDetail ports.MergeRequestDetail, err error) *MergeRequestService_Get_Call {
366366 _c.Call.Return(mergeRequestDetail, err)
367367 return _c
368368 }
369369
370-func (_c *MergeRequests_Get_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, sinceLastRead bool) (app.MergeRequestDetail, error)) *MergeRequests_Get_Call {
370+func (_c *MergeRequestService_Get_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, sinceLastRead bool) (ports.MergeRequestDetail, error)) *MergeRequestService_Get_Call {
371371 _c.Call.Return(run)
372372 return _c
373373 }
374374
375-// List provides a mock function for the type MergeRequests
376-func (_mock *MergeRequests) List(ctx context.Context, p domain.Principal, name domain.RepoName, params app.ListMergeRequestsParams) (app.MergeRequestPage, error) {
375+// List provides a mock function for the type MergeRequestService
376+func (_mock *MergeRequestService) List(ctx context.Context, p domain.Principal, name domain.RepoName, params ports.ListMergeRequestsParams) (ports.MergeRequestPage, error) {
377377 ret := _mock.Called(ctx, p, name, params)
378378
379379 if len(ret) == 0 {
380380 panic("no return value specified for List")
381381 }
382382
383- var r0 app.MergeRequestPage
383+ var r0 ports.MergeRequestPage
384384 var r1 error
385- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, app.ListMergeRequestsParams) (app.MergeRequestPage, error)); ok {
385+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, ports.ListMergeRequestsParams) (ports.MergeRequestPage, error)); ok {
386386 return returnFunc(ctx, p, name, params)
387387 }
388- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, app.ListMergeRequestsParams) app.MergeRequestPage); ok {
388+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, ports.ListMergeRequestsParams) ports.MergeRequestPage); ok {
389389 r0 = returnFunc(ctx, p, name, params)
390390 } else {
391- r0 = ret.Get(0).(app.MergeRequestPage)
391+ r0 = ret.Get(0).(ports.MergeRequestPage)
392392 }
393- if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, app.ListMergeRequestsParams) error); ok {
393+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, ports.ListMergeRequestsParams) error); ok {
394394 r1 = returnFunc(ctx, p, name, params)
395395 } else {
396396 r1 = ret.Error(1)
@@ -398,8 +398,8 @@ func (_mock *MergeRequests) List(ctx context.Context, p domain.Principal, name d
398398 return r0, r1
399399 }
400400
401-// MergeRequests_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List'
402-type MergeRequests_List_Call struct {
401+// MergeRequestService_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List'
402+type MergeRequestService_List_Call struct {
403403 *mock.Call
404404 }
405405
@@ -407,12 +407,12 @@ type MergeRequests_List_Call struct {
407407 // - ctx context.Context
408408 // - p domain.Principal
409409 // - name domain.RepoName
410-// - params app.ListMergeRequestsParams
411-func (_e *MergeRequests_Expecter) List(ctx any, p any, name any, params any) *MergeRequests_List_Call {
412- return &MergeRequests_List_Call{Call: _e.mock.On("List", ctx, p, name, params)}
410+// - params ports.ListMergeRequestsParams
411+func (_e *MergeRequestService_Expecter) List(ctx any, p any, name any, params any) *MergeRequestService_List_Call {
412+ return &MergeRequestService_List_Call{Call: _e.mock.On("List", ctx, p, name, params)}
413413 }
414414
415-func (_c *MergeRequests_List_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, params app.ListMergeRequestsParams)) *MergeRequests_List_Call {
415+func (_c *MergeRequestService_List_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, params ports.ListMergeRequestsParams)) *MergeRequestService_List_Call {
416416 _c.Call.Run(func(args mock.Arguments) {
417417 var arg0 context.Context
418418 if args[0] != nil {
@@ -426,9 +426,9 @@ func (_c *MergeRequests_List_Call) Run(run func(ctx context.Context, p domain.Pr
426426 if args[2] != nil {
427427 arg2 = args[2].(domain.RepoName)
428428 }
429- var arg3 app.ListMergeRequestsParams
429+ var arg3 ports.ListMergeRequestsParams
430430 if args[3] != nil {
431- arg3 = args[3].(app.ListMergeRequestsParams)
431+ arg3 = args[3].(ports.ListMergeRequestsParams)
432432 }
433433 run(
434434 arg0,
@@ -440,18 +440,18 @@ func (_c *MergeRequests_List_Call) Run(run func(ctx context.Context, p domain.Pr
440440 return _c
441441 }
442442
443-func (_c *MergeRequests_List_Call) Return(mergeRequestPage app.MergeRequestPage, err error) *MergeRequests_List_Call {
443+func (_c *MergeRequestService_List_Call) Return(mergeRequestPage ports.MergeRequestPage, err error) *MergeRequestService_List_Call {
444444 _c.Call.Return(mergeRequestPage, err)
445445 return _c
446446 }
447447
448-func (_c *MergeRequests_List_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, params app.ListMergeRequestsParams) (app.MergeRequestPage, error)) *MergeRequests_List_Call {
448+func (_c *MergeRequestService_List_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, params ports.ListMergeRequestsParams) (ports.MergeRequestPage, error)) *MergeRequestService_List_Call {
449449 _c.Call.Return(run)
450450 return _c
451451 }
452452
453-// MarkRead provides a mock function for the type MergeRequests
454-func (_mock *MergeRequests) MarkRead(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, head domain.ObjectID) (domain.ObjectID, error) {
453+// MarkRead provides a mock function for the type MergeRequestService
454+func (_mock *MergeRequestService) MarkRead(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, head domain.ObjectID) (domain.ObjectID, error) {
455455 ret := _mock.Called(ctx, p, name, number, head)
456456
457457 if len(ret) == 0 {
@@ -476,8 +476,8 @@ func (_mock *MergeRequests) MarkRead(ctx context.Context, p domain.Principal, na
476476 return r0, r1
477477 }
478478
479-// MergeRequests_MarkRead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarkRead'
480-type MergeRequests_MarkRead_Call struct {
479+// MergeRequestService_MarkRead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarkRead'
480+type MergeRequestService_MarkRead_Call struct {
481481 *mock.Call
482482 }
483483
@@ -487,11 +487,11 @@ type MergeRequests_MarkRead_Call struct {
487487 // - name domain.RepoName
488488 // - number int64
489489 // - head domain.ObjectID
490-func (_e *MergeRequests_Expecter) MarkRead(ctx any, p any, name any, number any, head any) *MergeRequests_MarkRead_Call {
491- return &MergeRequests_MarkRead_Call{Call: _e.mock.On("MarkRead", ctx, p, name, number, head)}
490+func (_e *MergeRequestService_Expecter) MarkRead(ctx any, p any, name any, number any, head any) *MergeRequestService_MarkRead_Call {
491+ return &MergeRequestService_MarkRead_Call{Call: _e.mock.On("MarkRead", ctx, p, name, number, head)}
492492 }
493493
494-func (_c *MergeRequests_MarkRead_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, head domain.ObjectID)) *MergeRequests_MarkRead_Call {
494+func (_c *MergeRequestService_MarkRead_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, head domain.ObjectID)) *MergeRequestService_MarkRead_Call {
495495 _c.Call.Run(func(args mock.Arguments) {
496496 var arg0 context.Context
497497 if args[0] != nil {
@@ -524,35 +524,35 @@ func (_c *MergeRequests_MarkRead_Call) Run(run func(ctx context.Context, p domai
524524 return _c
525525 }
526526
527-func (_c *MergeRequests_MarkRead_Call) Return(objectID domain.ObjectID, err error) *MergeRequests_MarkRead_Call {
527+func (_c *MergeRequestService_MarkRead_Call) Return(objectID domain.ObjectID, err error) *MergeRequestService_MarkRead_Call {
528528 _c.Call.Return(objectID, err)
529529 return _c
530530 }
531531
532-func (_c *MergeRequests_MarkRead_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, head domain.ObjectID) (domain.ObjectID, error)) *MergeRequests_MarkRead_Call {
532+func (_c *MergeRequestService_MarkRead_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, head domain.ObjectID) (domain.ObjectID, error)) *MergeRequestService_MarkRead_Call {
533533 _c.Call.Return(run)
534534 return _c
535535 }
536536
537-// Merge provides a mock function for the type MergeRequests
538-func (_mock *MergeRequests) Merge(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params app.MergeParams) (app.MergeResult, error) {
537+// Merge provides a mock function for the type MergeRequestService
538+func (_mock *MergeRequestService) Merge(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params ports.MergeParams) (ports.MergeResult, error) {
539539 ret := _mock.Called(ctx, p, name, number, params)
540540
541541 if len(ret) == 0 {
542542 panic("no return value specified for Merge")
543543 }
544544
545- var r0 app.MergeResult
545+ var r0 ports.MergeResult
546546 var r1 error
547- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64, app.MergeParams) (app.MergeResult, error)); ok {
547+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64, ports.MergeParams) (ports.MergeResult, error)); ok {
548548 return returnFunc(ctx, p, name, number, params)
549549 }
550- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64, app.MergeParams) app.MergeResult); ok {
550+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, int64, ports.MergeParams) ports.MergeResult); ok {
551551 r0 = returnFunc(ctx, p, name, number, params)
552552 } else {
553- r0 = ret.Get(0).(app.MergeResult)
553+ r0 = ret.Get(0).(ports.MergeResult)
554554 }
555- if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, int64, app.MergeParams) error); ok {
555+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, int64, ports.MergeParams) error); ok {
556556 r1 = returnFunc(ctx, p, name, number, params)
557557 } else {
558558 r1 = ret.Error(1)
@@ -560,8 +560,8 @@ func (_mock *MergeRequests) Merge(ctx context.Context, p domain.Principal, name
560560 return r0, r1
561561 }
562562
563-// MergeRequests_Merge_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Merge'
564-type MergeRequests_Merge_Call struct {
563+// MergeRequestService_Merge_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Merge'
564+type MergeRequestService_Merge_Call struct {
565565 *mock.Call
566566 }
567567
@@ -570,12 +570,12 @@ type MergeRequests_Merge_Call struct {
570570 // - p domain.Principal
571571 // - name domain.RepoName
572572 // - number int64
573-// - params app.MergeParams
574-func (_e *MergeRequests_Expecter) Merge(ctx any, p any, name any, number any, params any) *MergeRequests_Merge_Call {
575- return &MergeRequests_Merge_Call{Call: _e.mock.On("Merge", ctx, p, name, number, params)}
573+// - params ports.MergeParams
574+func (_e *MergeRequestService_Expecter) Merge(ctx any, p any, name any, number any, params any) *MergeRequestService_Merge_Call {
575+ return &MergeRequestService_Merge_Call{Call: _e.mock.On("Merge", ctx, p, name, number, params)}
576576 }
577577
578-func (_c *MergeRequests_Merge_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params app.MergeParams)) *MergeRequests_Merge_Call {
578+func (_c *MergeRequestService_Merge_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params ports.MergeParams)) *MergeRequestService_Merge_Call {
579579 _c.Call.Run(func(args mock.Arguments) {
580580 var arg0 context.Context
581581 if args[0] != nil {
@@ -593,9 +593,9 @@ func (_c *MergeRequests_Merge_Call) Run(run func(ctx context.Context, p domain.P
593593 if args[3] != nil {
594594 arg3 = args[3].(int64)
595595 }
596- var arg4 app.MergeParams
596+ var arg4 ports.MergeParams
597597 if args[4] != nil {
598- arg4 = args[4].(app.MergeParams)
598+ arg4 = args[4].(ports.MergeParams)
599599 }
600600 run(
601601 arg0,
@@ -608,18 +608,18 @@ func (_c *MergeRequests_Merge_Call) Run(run func(ctx context.Context, p domain.P
608608 return _c
609609 }
610610
611-func (_c *MergeRequests_Merge_Call) Return(mergeResult app.MergeResult, err error) *MergeRequests_Merge_Call {
611+func (_c *MergeRequestService_Merge_Call) Return(mergeResult ports.MergeResult, err error) *MergeRequestService_Merge_Call {
612612 _c.Call.Return(mergeResult, err)
613613 return _c
614614 }
615615
616-func (_c *MergeRequests_Merge_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params app.MergeParams) (app.MergeResult, error)) *MergeRequests_Merge_Call {
616+func (_c *MergeRequestService_Merge_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params ports.MergeParams) (ports.MergeResult, error)) *MergeRequestService_Merge_Call {
617617 _c.Call.Return(run)
618618 return _c
619619 }
620620
621-// ResolveComment provides a mock function for the type MergeRequests
622-func (_mock *MergeRequests) ResolveComment(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, commentID int64, resolved bool) (domain.Comment, error) {
621+// ResolveComment provides a mock function for the type MergeRequestService
622+func (_mock *MergeRequestService) ResolveComment(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, commentID int64, resolved bool) (domain.Comment, error) {
623623 ret := _mock.Called(ctx, p, name, number, commentID, resolved)
624624
625625 if len(ret) == 0 {
@@ -644,8 +644,8 @@ func (_mock *MergeRequests) ResolveComment(ctx context.Context, p domain.Princip
644644 return r0, r1
645645 }
646646
647-// MergeRequests_ResolveComment_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ResolveComment'
648-type MergeRequests_ResolveComment_Call struct {
647+// MergeRequestService_ResolveComment_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ResolveComment'
648+type MergeRequestService_ResolveComment_Call struct {
649649 *mock.Call
650650 }
651651
@@ -656,11 +656,11 @@ type MergeRequests_ResolveComment_Call struct {
656656 // - number int64
657657 // - commentID int64
658658 // - resolved bool
659-func (_e *MergeRequests_Expecter) ResolveComment(ctx any, p any, name any, number any, commentID any, resolved any) *MergeRequests_ResolveComment_Call {
660- return &MergeRequests_ResolveComment_Call{Call: _e.mock.On("ResolveComment", ctx, p, name, number, commentID, resolved)}
659+func (_e *MergeRequestService_Expecter) ResolveComment(ctx any, p any, name any, number any, commentID any, resolved any) *MergeRequestService_ResolveComment_Call {
660+ return &MergeRequestService_ResolveComment_Call{Call: _e.mock.On("ResolveComment", ctx, p, name, number, commentID, resolved)}
661661 }
662662
663-func (_c *MergeRequests_ResolveComment_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, commentID int64, resolved bool)) *MergeRequests_ResolveComment_Call {
663+func (_c *MergeRequestService_ResolveComment_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, commentID int64, resolved bool)) *MergeRequestService_ResolveComment_Call {
664664 _c.Call.Run(func(args mock.Arguments) {
665665 var arg0 context.Context
666666 if args[0] != nil {
@@ -698,12 +698,12 @@ func (_c *MergeRequests_ResolveComment_Call) Run(run func(ctx context.Context, p
698698 return _c
699699 }
700700
701-func (_c *MergeRequests_ResolveComment_Call) Return(comment domain.Comment, err error) *MergeRequests_ResolveComment_Call {
701+func (_c *MergeRequestService_ResolveComment_Call) Return(comment domain.Comment, err error) *MergeRequestService_ResolveComment_Call {
702702 _c.Call.Return(comment, err)
703703 return _c
704704 }
705705
706-func (_c *MergeRequests_ResolveComment_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, commentID int64, resolved bool) (domain.Comment, error)) *MergeRequests_ResolveComment_Call {
706+func (_c *MergeRequestService_ResolveComment_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, commentID int64, resolved bool) (domain.Comment, error)) *MergeRequestService_ResolveComment_Call {
707707 _c.Call.Return(run)
708708 return _c
709709 }
R internal/adapters/githttp/mocks/PushHooks.go → internal/ports/mocks/PushHookService.go +27 -27 renamed, 51% similar
@@ -11,17 +11,17 @@ import (
1111 mock "github.com/stretchr/testify/mock"
1212 )
1313
14-// NewPushHooks creates a new instance of PushHooks. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
14+// NewPushHookService creates a new instance of PushHookService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
1515 // The first argument is typically a *testing.T value.
16-func NewPushHooks(t interface {
16+func NewPushHookService(t interface {
1717 mock.TestingT
1818 Cleanup(func())
19-}) *PushHooks {
19+}) *PushHookService {
2020 if helper, ok := t.(interface{ Helper() }); ok {
2121 helper.Helper()
2222 }
2323
24- mock := &PushHooks{}
24+ mock := &PushHookService{}
2525 mock.Mock.Test(t)
2626
2727 t.Cleanup(func() {
@@ -34,21 +34,21 @@ func NewPushHooks(t interface {
3434 return mock
3535 }
3636
37-// PushHooks is an autogenerated mock type for the PushHooks type
38-type PushHooks struct {
37+// PushHookService is an autogenerated mock type for the PushHookService type
38+type PushHookService struct {
3939 mock.Mock
4040 }
4141
42-type PushHooks_Expecter struct {
42+type PushHookService_Expecter struct {
4343 mock *mock.Mock
4444 }
4545
46-func (_m *PushHooks) EXPECT() *PushHooks_Expecter {
47- return &PushHooks_Expecter{mock: &_m.Mock}
46+func (_m *PushHookService) EXPECT() *PushHookService_Expecter {
47+ return &PushHookService_Expecter{mock: &_m.Mock}
4848 }
4949
50-// Check provides a mock function for the type PushHooks
51-func (_mock *PushHooks) Check(ctx context.Context, p domain.Principal, name domain.RepoName, updates []domain.RefUpdate) error {
50+// Check provides a mock function for the type PushHookService
51+func (_mock *PushHookService) Check(ctx context.Context, p domain.Principal, name domain.RepoName, updates []domain.RefUpdate) error {
5252 ret := _mock.Called(ctx, p, name, updates)
5353
5454 if len(ret) == 0 {
@@ -64,8 +64,8 @@ func (_mock *PushHooks) Check(ctx context.Context, p domain.Principal, name doma
6464 return r0
6565 }
6666
67-// PushHooks_Check_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Check'
68-type PushHooks_Check_Call struct {
67+// PushHookService_Check_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Check'
68+type PushHookService_Check_Call struct {
6969 *mock.Call
7070 }
7171
@@ -74,11 +74,11 @@ type PushHooks_Check_Call struct {
7474 // - p domain.Principal
7575 // - name domain.RepoName
7676 // - updates []domain.RefUpdate
77-func (_e *PushHooks_Expecter) Check(ctx any, p any, name any, updates any) *PushHooks_Check_Call {
78- return &PushHooks_Check_Call{Call: _e.mock.On("Check", ctx, p, name, updates)}
77+func (_e *PushHookService_Expecter) Check(ctx any, p any, name any, updates any) *PushHookService_Check_Call {
78+ return &PushHookService_Check_Call{Call: _e.mock.On("Check", ctx, p, name, updates)}
7979 }
8080
81-func (_c *PushHooks_Check_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, updates []domain.RefUpdate)) *PushHooks_Check_Call {
81+func (_c *PushHookService_Check_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, updates []domain.RefUpdate)) *PushHookService_Check_Call {
8282 _c.Call.Run(func(args mock.Arguments) {
8383 var arg0 context.Context
8484 if args[0] != nil {
@@ -106,18 +106,18 @@ func (_c *PushHooks_Check_Call) Run(run func(ctx context.Context, p domain.Princ
106106 return _c
107107 }
108108
109-func (_c *PushHooks_Check_Call) Return(err error) *PushHooks_Check_Call {
109+func (_c *PushHookService_Check_Call) Return(err error) *PushHookService_Check_Call {
110110 _c.Call.Return(err)
111111 return _c
112112 }
113113
114-func (_c *PushHooks_Check_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, updates []domain.RefUpdate) error) *PushHooks_Check_Call {
114+func (_c *PushHookService_Check_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, updates []domain.RefUpdate) error) *PushHookService_Check_Call {
115115 _c.Call.Return(run)
116116 return _c
117117 }
118118
119-// Record provides a mock function for the type PushHooks
120-func (_mock *PushHooks) Record(ctx context.Context, p domain.Principal, name domain.RepoName, updates []domain.RefUpdate) error {
119+// Record provides a mock function for the type PushHookService
120+func (_mock *PushHookService) Record(ctx context.Context, p domain.Principal, name domain.RepoName, updates []domain.RefUpdate) error {
121121 ret := _mock.Called(ctx, p, name, updates)
122122
123123 if len(ret) == 0 {
@@ -133,8 +133,8 @@ func (_mock *PushHooks) Record(ctx context.Context, p domain.Principal, name dom
133133 return r0
134134 }
135135
136-// PushHooks_Record_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Record'
137-type PushHooks_Record_Call struct {
136+// PushHookService_Record_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Record'
137+type PushHookService_Record_Call struct {
138138 *mock.Call
139139 }
140140
@@ -143,11 +143,11 @@ type PushHooks_Record_Call struct {
143143 // - p domain.Principal
144144 // - name domain.RepoName
145145 // - updates []domain.RefUpdate
146-func (_e *PushHooks_Expecter) Record(ctx any, p any, name any, updates any) *PushHooks_Record_Call {
147- return &PushHooks_Record_Call{Call: _e.mock.On("Record", ctx, p, name, updates)}
146+func (_e *PushHookService_Expecter) Record(ctx any, p any, name any, updates any) *PushHookService_Record_Call {
147+ return &PushHookService_Record_Call{Call: _e.mock.On("Record", ctx, p, name, updates)}
148148 }
149149
150-func (_c *PushHooks_Record_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, updates []domain.RefUpdate)) *PushHooks_Record_Call {
150+func (_c *PushHookService_Record_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, updates []domain.RefUpdate)) *PushHookService_Record_Call {
151151 _c.Call.Run(func(args mock.Arguments) {
152152 var arg0 context.Context
153153 if args[0] != nil {
@@ -175,12 +175,12 @@ func (_c *PushHooks_Record_Call) Run(run func(ctx context.Context, p domain.Prin
175175 return _c
176176 }
177177
178-func (_c *PushHooks_Record_Call) Return(err error) *PushHooks_Record_Call {
178+func (_c *PushHookService_Record_Call) Return(err error) *PushHookService_Record_Call {
179179 _c.Call.Return(err)
180180 return _c
181181 }
182182
183-func (_c *PushHooks_Record_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, updates []domain.RefUpdate) error) *PushHooks_Record_Call {
183+func (_c *PushHookService_Record_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, updates []domain.RefUpdate) error) *PushHookService_Record_Call {
184184 _c.Call.Return(run)
185185 return _c
186186 }
R internal/adapters/githttp/mocks/Pushes.go → internal/ports/mocks/PushService.go +18 -18 renamed, 53% similar
@@ -11,17 +11,17 @@ import (
1111 mock "github.com/stretchr/testify/mock"
1212 )
1313
14-// NewPushes creates a new instance of Pushes. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
14+// NewPushService creates a new instance of PushService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
1515 // The first argument is typically a *testing.T value.
16-func NewPushes(t interface {
16+func NewPushService(t interface {
1717 mock.TestingT
1818 Cleanup(func())
19-}) *Pushes {
19+}) *PushService {
2020 if helper, ok := t.(interface{ Helper() }); ok {
2121 helper.Helper()
2222 }
2323
24- mock := &Pushes{}
24+ mock := &PushService{}
2525 mock.Mock.Test(t)
2626
2727 t.Cleanup(func() {
@@ -34,21 +34,21 @@ func NewPushes(t interface {
3434 return mock
3535 }
3636
37-// Pushes is an autogenerated mock type for the Pushes type
38-type Pushes struct {
37+// PushService is an autogenerated mock type for the PushService type
38+type PushService struct {
3939 mock.Mock
4040 }
4141
42-type Pushes_Expecter struct {
42+type PushService_Expecter struct {
4343 mock *mock.Mock
4444 }
4545
46-func (_m *Pushes) EXPECT() *Pushes_Expecter {
47- return &Pushes_Expecter{mock: &_m.Mock}
46+func (_m *PushService) EXPECT() *PushService_Expecter {
47+ return &PushService_Expecter{mock: &_m.Mock}
4848 }
4949
50-// Open provides a mock function for the type Pushes
51-func (_mock *Pushes) Open(ctx context.Context, p domain.Principal, name domain.RepoName, create bool) error {
50+// Open provides a mock function for the type PushService
51+func (_mock *PushService) Open(ctx context.Context, p domain.Principal, name domain.RepoName, create bool) error {
5252 ret := _mock.Called(ctx, p, name, create)
5353
5454 if len(ret) == 0 {
@@ -64,8 +64,8 @@ func (_mock *Pushes) Open(ctx context.Context, p domain.Principal, name domain.R
6464 return r0
6565 }
6666
67-// Pushes_Open_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Open'
68-type Pushes_Open_Call struct {
67+// PushService_Open_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Open'
68+type PushService_Open_Call struct {
6969 *mock.Call
7070 }
7171
@@ -74,11 +74,11 @@ type Pushes_Open_Call struct {
7474 // - p domain.Principal
7575 // - name domain.RepoName
7676 // - create bool
77-func (_e *Pushes_Expecter) Open(ctx any, p any, name any, create any) *Pushes_Open_Call {
78- return &Pushes_Open_Call{Call: _e.mock.On("Open", ctx, p, name, create)}
77+func (_e *PushService_Expecter) Open(ctx any, p any, name any, create any) *PushService_Open_Call {
78+ return &PushService_Open_Call{Call: _e.mock.On("Open", ctx, p, name, create)}
7979 }
8080
81-func (_c *Pushes_Open_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, create bool)) *Pushes_Open_Call {
81+func (_c *PushService_Open_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, create bool)) *PushService_Open_Call {
8282 _c.Call.Run(func(args mock.Arguments) {
8383 var arg0 context.Context
8484 if args[0] != nil {
@@ -106,12 +106,12 @@ func (_c *Pushes_Open_Call) Run(run func(ctx context.Context, p domain.Principal
106106 return _c
107107 }
108108
109-func (_c *Pushes_Open_Call) Return(err error) *Pushes_Open_Call {
109+func (_c *PushService_Open_Call) Return(err error) *PushService_Open_Call {
110110 _c.Call.Return(err)
111111 return _c
112112 }
113113
114-func (_c *Pushes_Open_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, create bool) error) *Pushes_Open_Call {
114+func (_c *PushService_Open_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, create bool) error) *PushService_Open_Call {
115115 _c.Call.Return(run)
116116 return _c
117117 }
R internal/adapters/githttp/mocks/Repos.go → internal/ports/mocks/RepoLookup.go +18 -18 renamed, 58% similar
@@ -11,17 +11,17 @@ import (
1111 mock "github.com/stretchr/testify/mock"
1212 )
1313
14-// NewRepos creates a new instance of Repos. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
14+// NewRepoLookup creates a new instance of RepoLookup. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
1515 // The first argument is typically a *testing.T value.
16-func NewRepos(t interface {
16+func NewRepoLookup(t interface {
1717 mock.TestingT
1818 Cleanup(func())
19-}) *Repos {
19+}) *RepoLookup {
2020 if helper, ok := t.(interface{ Helper() }); ok {
2121 helper.Helper()
2222 }
2323
24- mock := &Repos{}
24+ mock := &RepoLookup{}
2525 mock.Mock.Test(t)
2626
2727 t.Cleanup(func() {
@@ -34,21 +34,21 @@ func NewRepos(t interface {
3434 return mock
3535 }
3636
37-// Repos is an autogenerated mock type for the Repos type
38-type Repos struct {
37+// RepoLookup is an autogenerated mock type for the RepoLookup type
38+type RepoLookup struct {
3939 mock.Mock
4040 }
4141
42-type Repos_Expecter struct {
42+type RepoLookup_Expecter struct {
4343 mock *mock.Mock
4444 }
4545
46-func (_m *Repos) EXPECT() *Repos_Expecter {
47- return &Repos_Expecter{mock: &_m.Mock}
46+func (_m *RepoLookup) EXPECT() *RepoLookup_Expecter {
47+ return &RepoLookup_Expecter{mock: &_m.Mock}
4848 }
4949
50-// Get provides a mock function for the type Repos
51-func (_mock *Repos) Get(ctx context.Context, p domain.Principal, name domain.RepoName) (domain.Repo, error) {
50+// Get provides a mock function for the type RepoLookup
51+func (_mock *RepoLookup) Get(ctx context.Context, p domain.Principal, name domain.RepoName) (domain.Repo, error) {
5252 ret := _mock.Called(ctx, p, name)
5353
5454 if len(ret) == 0 {
@@ -73,8 +73,8 @@ func (_mock *Repos) Get(ctx context.Context, p domain.Principal, name domain.Rep
7373 return r0, r1
7474 }
7575
76-// Repos_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get'
77-type Repos_Get_Call struct {
76+// RepoLookup_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get'
77+type RepoLookup_Get_Call struct {
7878 *mock.Call
7979 }
8080
@@ -82,11 +82,11 @@ type Repos_Get_Call struct {
8282 // - ctx context.Context
8383 // - p domain.Principal
8484 // - name domain.RepoName
85-func (_e *Repos_Expecter) Get(ctx any, p any, name any) *Repos_Get_Call {
86- return &Repos_Get_Call{Call: _e.mock.On("Get", ctx, p, name)}
85+func (_e *RepoLookup_Expecter) Get(ctx any, p any, name any) *RepoLookup_Get_Call {
86+ return &RepoLookup_Get_Call{Call: _e.mock.On("Get", ctx, p, name)}
8787 }
8888
89-func (_c *Repos_Get_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName)) *Repos_Get_Call {
89+func (_c *RepoLookup_Get_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName)) *RepoLookup_Get_Call {
9090 _c.Call.Run(func(args mock.Arguments) {
9191 var arg0 context.Context
9292 if args[0] != nil {
@@ -109,12 +109,12 @@ func (_c *Repos_Get_Call) Run(run func(ctx context.Context, p domain.Principal,
109109 return _c
110110 }
111111
112-func (_c *Repos_Get_Call) Return(repo domain.Repo, err error) *Repos_Get_Call {
112+func (_c *RepoLookup_Get_Call) Return(repo domain.Repo, err error) *RepoLookup_Get_Call {
113113 _c.Call.Return(repo, err)
114114 return _c
115115 }
116116
117-func (_c *Repos_Get_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName) (domain.Repo, error)) *Repos_Get_Call {
117+func (_c *RepoLookup_Get_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName) (domain.Repo, error)) *RepoLookup_Get_Call {
118118 _c.Call.Return(run)
119119 return _c
120120 }
R internal/adapters/rpc/mocks/Repos.go → internal/ports/mocks/RepoService.go +84 -84 renamed, 50% similar
@@ -7,22 +7,22 @@ package mocks
77 import (
88 "context"
99
10- "bnema.dev/ermite/internal/app"
1110 "bnema.dev/ermite/internal/domain"
11+ "bnema.dev/ermite/internal/ports"
1212 mock "github.com/stretchr/testify/mock"
1313 )
1414
15-// NewRepos creates a new instance of Repos. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
15+// NewRepoService creates a new instance of RepoService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
1616 // The first argument is typically a *testing.T value.
17-func NewRepos(t interface {
17+func NewRepoService(t interface {
1818 mock.TestingT
1919 Cleanup(func())
20-}) *Repos {
20+}) *RepoService {
2121 if helper, ok := t.(interface{ Helper() }); ok {
2222 helper.Helper()
2323 }
2424
25- mock := &Repos{}
25+ mock := &RepoService{}
2626 mock.Mock.Test(t)
2727
2828 t.Cleanup(func() {
@@ -35,21 +35,21 @@ func NewRepos(t interface {
3535 return mock
3636 }
3737
38-// Repos is an autogenerated mock type for the Repos type
39-type Repos struct {
38+// RepoService is an autogenerated mock type for the RepoService type
39+type RepoService struct {
4040 mock.Mock
4141 }
4242
43-type Repos_Expecter struct {
43+type RepoService_Expecter struct {
4444 mock *mock.Mock
4545 }
4646
47-func (_m *Repos) EXPECT() *Repos_Expecter {
48- return &Repos_Expecter{mock: &_m.Mock}
47+func (_m *RepoService) EXPECT() *RepoService_Expecter {
48+ return &RepoService_Expecter{mock: &_m.Mock}
4949 }
5050
51-// Create provides a mock function for the type Repos
52-func (_mock *Repos) Create(ctx context.Context, p domain.Principal, params app.CreateParams) (domain.Repo, error) {
51+// Create provides a mock function for the type RepoService
52+func (_mock *RepoService) Create(ctx context.Context, p domain.Principal, params ports.CreateRepoParams) (domain.Repo, error) {
5353 ret := _mock.Called(ctx, p, params)
5454
5555 if len(ret) == 0 {
@@ -58,15 +58,15 @@ func (_mock *Repos) Create(ctx context.Context, p domain.Principal, params app.C
5858
5959 var r0 domain.Repo
6060 var r1 error
61- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, app.CreateParams) (domain.Repo, error)); ok {
61+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, ports.CreateRepoParams) (domain.Repo, error)); ok {
6262 return returnFunc(ctx, p, params)
6363 }
64- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, app.CreateParams) domain.Repo); ok {
64+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, ports.CreateRepoParams) domain.Repo); ok {
6565 r0 = returnFunc(ctx, p, params)
6666 } else {
6767 r0 = ret.Get(0).(domain.Repo)
6868 }
69- if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, app.CreateParams) error); ok {
69+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, ports.CreateRepoParams) error); ok {
7070 r1 = returnFunc(ctx, p, params)
7171 } else {
7272 r1 = ret.Error(1)
@@ -74,20 +74,20 @@ func (_mock *Repos) Create(ctx context.Context, p domain.Principal, params app.C
7474 return r0, r1
7575 }
7676
77-// Repos_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create'
78-type Repos_Create_Call struct {
77+// RepoService_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create'
78+type RepoService_Create_Call struct {
7979 *mock.Call
8080 }
8181
8282 // Create is a helper method to define mock.On call
8383 // - ctx context.Context
8484 // - p domain.Principal
85-// - params app.CreateParams
86-func (_e *Repos_Expecter) Create(ctx any, p any, params any) *Repos_Create_Call {
87- return &Repos_Create_Call{Call: _e.mock.On("Create", ctx, p, params)}
85+// - params ports.CreateRepoParams
86+func (_e *RepoService_Expecter) Create(ctx any, p any, params any) *RepoService_Create_Call {
87+ return &RepoService_Create_Call{Call: _e.mock.On("Create", ctx, p, params)}
8888 }
8989
90-func (_c *Repos_Create_Call) Run(run func(ctx context.Context, p domain.Principal, params app.CreateParams)) *Repos_Create_Call {
90+func (_c *RepoService_Create_Call) Run(run func(ctx context.Context, p domain.Principal, params ports.CreateRepoParams)) *RepoService_Create_Call {
9191 _c.Call.Run(func(args mock.Arguments) {
9292 var arg0 context.Context
9393 if args[0] != nil {
@@ -97,9 +97,9 @@ func (_c *Repos_Create_Call) Run(run func(ctx context.Context, p domain.Principa
9797 if args[1] != nil {
9898 arg1 = args[1].(domain.Principal)
9999 }
100- var arg2 app.CreateParams
100+ var arg2 ports.CreateRepoParams
101101 if args[2] != nil {
102- arg2 = args[2].(app.CreateParams)
102+ arg2 = args[2].(ports.CreateRepoParams)
103103 }
104104 run(
105105 arg0,
@@ -110,18 +110,18 @@ func (_c *Repos_Create_Call) Run(run func(ctx context.Context, p domain.Principa
110110 return _c
111111 }
112112
113-func (_c *Repos_Create_Call) Return(repo domain.Repo, err error) *Repos_Create_Call {
113+func (_c *RepoService_Create_Call) Return(repo domain.Repo, err error) *RepoService_Create_Call {
114114 _c.Call.Return(repo, err)
115115 return _c
116116 }
117117
118-func (_c *Repos_Create_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, params app.CreateParams) (domain.Repo, error)) *Repos_Create_Call {
118+func (_c *RepoService_Create_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, params ports.CreateRepoParams) (domain.Repo, error)) *RepoService_Create_Call {
119119 _c.Call.Return(run)
120120 return _c
121121 }
122122
123-// Delete provides a mock function for the type Repos
124-func (_mock *Repos) Delete(ctx context.Context, p domain.Principal, name domain.RepoName) error {
123+// Delete provides a mock function for the type RepoService
124+func (_mock *RepoService) Delete(ctx context.Context, p domain.Principal, name domain.RepoName) error {
125125 ret := _mock.Called(ctx, p, name)
126126
127127 if len(ret) == 0 {
@@ -137,8 +137,8 @@ func (_mock *Repos) Delete(ctx context.Context, p domain.Principal, name domain.
137137 return r0
138138 }
139139
140-// Repos_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete'
141-type Repos_Delete_Call struct {
140+// RepoService_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete'
141+type RepoService_Delete_Call struct {
142142 *mock.Call
143143 }
144144
@@ -146,11 +146,11 @@ type Repos_Delete_Call struct {
146146 // - ctx context.Context
147147 // - p domain.Principal
148148 // - name domain.RepoName
149-func (_e *Repos_Expecter) Delete(ctx any, p any, name any) *Repos_Delete_Call {
150- return &Repos_Delete_Call{Call: _e.mock.On("Delete", ctx, p, name)}
149+func (_e *RepoService_Expecter) Delete(ctx any, p any, name any) *RepoService_Delete_Call {
150+ return &RepoService_Delete_Call{Call: _e.mock.On("Delete", ctx, p, name)}
151151 }
152152
153-func (_c *Repos_Delete_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName)) *Repos_Delete_Call {
153+func (_c *RepoService_Delete_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName)) *RepoService_Delete_Call {
154154 _c.Call.Run(func(args mock.Arguments) {
155155 var arg0 context.Context
156156 if args[0] != nil {
@@ -173,18 +173,18 @@ func (_c *Repos_Delete_Call) Run(run func(ctx context.Context, p domain.Principa
173173 return _c
174174 }
175175
176-func (_c *Repos_Delete_Call) Return(err error) *Repos_Delete_Call {
176+func (_c *RepoService_Delete_Call) Return(err error) *RepoService_Delete_Call {
177177 _c.Call.Return(err)
178178 return _c
179179 }
180180
181-func (_c *Repos_Delete_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName) error) *Repos_Delete_Call {
181+func (_c *RepoService_Delete_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName) error) *RepoService_Delete_Call {
182182 _c.Call.Return(run)
183183 return _c
184184 }
185185
186-// Get provides a mock function for the type Repos
187-func (_mock *Repos) Get(ctx context.Context, p domain.Principal, name domain.RepoName) (domain.Repo, error) {
186+// Get provides a mock function for the type RepoService
187+func (_mock *RepoService) Get(ctx context.Context, p domain.Principal, name domain.RepoName) (domain.Repo, error) {
188188 ret := _mock.Called(ctx, p, name)
189189
190190 if len(ret) == 0 {
@@ -209,8 +209,8 @@ func (_mock *Repos) Get(ctx context.Context, p domain.Principal, name domain.Rep
209209 return r0, r1
210210 }
211211
212-// Repos_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get'
213-type Repos_Get_Call struct {
212+// RepoService_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get'
213+type RepoService_Get_Call struct {
214214 *mock.Call
215215 }
216216
@@ -218,11 +218,11 @@ type Repos_Get_Call struct {
218218 // - ctx context.Context
219219 // - p domain.Principal
220220 // - name domain.RepoName
221-func (_e *Repos_Expecter) Get(ctx any, p any, name any) *Repos_Get_Call {
222- return &Repos_Get_Call{Call: _e.mock.On("Get", ctx, p, name)}
221+func (_e *RepoService_Expecter) Get(ctx any, p any, name any) *RepoService_Get_Call {
222+ return &RepoService_Get_Call{Call: _e.mock.On("Get", ctx, p, name)}
223223 }
224224
225-func (_c *Repos_Get_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName)) *Repos_Get_Call {
225+func (_c *RepoService_Get_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName)) *RepoService_Get_Call {
226226 _c.Call.Run(func(args mock.Arguments) {
227227 var arg0 context.Context
228228 if args[0] != nil {
@@ -245,33 +245,33 @@ func (_c *Repos_Get_Call) Run(run func(ctx context.Context, p domain.Principal,
245245 return _c
246246 }
247247
248-func (_c *Repos_Get_Call) Return(repo domain.Repo, err error) *Repos_Get_Call {
248+func (_c *RepoService_Get_Call) Return(repo domain.Repo, err error) *RepoService_Get_Call {
249249 _c.Call.Return(repo, err)
250250 return _c
251251 }
252252
253-func (_c *Repos_Get_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName) (domain.Repo, error)) *Repos_Get_Call {
253+func (_c *RepoService_Get_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName) (domain.Repo, error)) *RepoService_Get_Call {
254254 _c.Call.Return(run)
255255 return _c
256256 }
257257
258-// ListPage provides a mock function for the type Repos
259-func (_mock *Repos) ListPage(ctx context.Context, p domain.Principal, size int, token string) (app.RepoPage, error) {
258+// ListPage provides a mock function for the type RepoService
259+func (_mock *RepoService) ListPage(ctx context.Context, p domain.Principal, size int, token string) (ports.RepoPage, error) {
260260 ret := _mock.Called(ctx, p, size, token)
261261
262262 if len(ret) == 0 {
263263 panic("no return value specified for ListPage")
264264 }
265265
266- var r0 app.RepoPage
266+ var r0 ports.RepoPage
267267 var r1 error
268- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, int, string) (app.RepoPage, error)); ok {
268+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, int, string) (ports.RepoPage, error)); ok {
269269 return returnFunc(ctx, p, size, token)
270270 }
271- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, int, string) app.RepoPage); ok {
271+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, int, string) ports.RepoPage); ok {
272272 r0 = returnFunc(ctx, p, size, token)
273273 } else {
274- r0 = ret.Get(0).(app.RepoPage)
274+ r0 = ret.Get(0).(ports.RepoPage)
275275 }
276276 if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, int, string) error); ok {
277277 r1 = returnFunc(ctx, p, size, token)
@@ -281,8 +281,8 @@ func (_mock *Repos) ListPage(ctx context.Context, p domain.Principal, size int,
281281 return r0, r1
282282 }
283283
284-// Repos_ListPage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListPage'
285-type Repos_ListPage_Call struct {
284+// RepoService_ListPage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListPage'
285+type RepoService_ListPage_Call struct {
286286 *mock.Call
287287 }
288288
@@ -291,11 +291,11 @@ type Repos_ListPage_Call struct {
291291 // - p domain.Principal
292292 // - size int
293293 // - token string
294-func (_e *Repos_Expecter) ListPage(ctx any, p any, size any, token any) *Repos_ListPage_Call {
295- return &Repos_ListPage_Call{Call: _e.mock.On("ListPage", ctx, p, size, token)}
294+func (_e *RepoService_Expecter) ListPage(ctx any, p any, size any, token any) *RepoService_ListPage_Call {
295+ return &RepoService_ListPage_Call{Call: _e.mock.On("ListPage", ctx, p, size, token)}
296296 }
297297
298-func (_c *Repos_ListPage_Call) Run(run func(ctx context.Context, p domain.Principal, size int, token string)) *Repos_ListPage_Call {
298+func (_c *RepoService_ListPage_Call) Run(run func(ctx context.Context, p domain.Principal, size int, token string)) *RepoService_ListPage_Call {
299299 _c.Call.Run(func(args mock.Arguments) {
300300 var arg0 context.Context
301301 if args[0] != nil {
@@ -323,18 +323,18 @@ func (_c *Repos_ListPage_Call) Run(run func(ctx context.Context, p domain.Princi
323323 return _c
324324 }
325325
326-func (_c *Repos_ListPage_Call) Return(repoPage app.RepoPage, err error) *Repos_ListPage_Call {
326+func (_c *RepoService_ListPage_Call) Return(repoPage ports.RepoPage, err error) *RepoService_ListPage_Call {
327327 _c.Call.Return(repoPage, err)
328328 return _c
329329 }
330330
331-func (_c *Repos_ListPage_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, size int, token string) (app.RepoPage, error)) *Repos_ListPage_Call {
331+func (_c *RepoService_ListPage_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, size int, token string) (ports.RepoPage, error)) *RepoService_ListPage_Call {
332332 _c.Call.Return(run)
333333 return _c
334334 }
335335
336-// Update provides a mock function for the type Repos
337-func (_mock *Repos) Update(ctx context.Context, p domain.Principal, name domain.RepoName, params app.UpdateParams) (domain.Repo, error) {
336+// Update provides a mock function for the type RepoService
337+func (_mock *RepoService) Update(ctx context.Context, p domain.Principal, name domain.RepoName, params ports.UpdateRepoParams) (domain.Repo, error) {
338338 ret := _mock.Called(ctx, p, name, params)
339339
340340 if len(ret) == 0 {
@@ -343,15 +343,15 @@ func (_mock *Repos) Update(ctx context.Context, p domain.Principal, name domain.
343343
344344 var r0 domain.Repo
345345 var r1 error
346- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, app.UpdateParams) (domain.Repo, error)); ok {
346+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, ports.UpdateRepoParams) (domain.Repo, error)); ok {
347347 return returnFunc(ctx, p, name, params)
348348 }
349- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, app.UpdateParams) domain.Repo); ok {
349+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, ports.UpdateRepoParams) domain.Repo); ok {
350350 r0 = returnFunc(ctx, p, name, params)
351351 } else {
352352 r0 = ret.Get(0).(domain.Repo)
353353 }
354- if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, app.UpdateParams) error); ok {
354+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, ports.UpdateRepoParams) error); ok {
355355 r1 = returnFunc(ctx, p, name, params)
356356 } else {
357357 r1 = ret.Error(1)
@@ -359,8 +359,8 @@ func (_mock *Repos) Update(ctx context.Context, p domain.Principal, name domain.
359359 return r0, r1
360360 }
361361
362-// Repos_Update_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Update'
363-type Repos_Update_Call struct {
362+// RepoService_Update_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Update'
363+type RepoService_Update_Call struct {
364364 *mock.Call
365365 }
366366
@@ -368,12 +368,12 @@ type Repos_Update_Call struct {
368368 // - ctx context.Context
369369 // - p domain.Principal
370370 // - name domain.RepoName
371-// - params app.UpdateParams
372-func (_e *Repos_Expecter) Update(ctx any, p any, name any, params any) *Repos_Update_Call {
373- return &Repos_Update_Call{Call: _e.mock.On("Update", ctx, p, name, params)}
371+// - params ports.UpdateRepoParams
372+func (_e *RepoService_Expecter) Update(ctx any, p any, name any, params any) *RepoService_Update_Call {
373+ return &RepoService_Update_Call{Call: _e.mock.On("Update", ctx, p, name, params)}
374374 }
375375
376-func (_c *Repos_Update_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, params app.UpdateParams)) *Repos_Update_Call {
376+func (_c *RepoService_Update_Call) Run(run func(ctx context.Context, p domain.Principal, name domain.RepoName, params ports.UpdateRepoParams)) *RepoService_Update_Call {
377377 _c.Call.Run(func(args mock.Arguments) {
378378 var arg0 context.Context
379379 if args[0] != nil {
@@ -387,9 +387,9 @@ func (_c *Repos_Update_Call) Run(run func(ctx context.Context, p domain.Principa
387387 if args[2] != nil {
388388 arg2 = args[2].(domain.RepoName)
389389 }
390- var arg3 app.UpdateParams
390+ var arg3 ports.UpdateRepoParams
391391 if args[3] != nil {
392- arg3 = args[3].(app.UpdateParams)
392+ arg3 = args[3].(ports.UpdateRepoParams)
393393 }
394394 run(
395395 arg0,
@@ -401,33 +401,33 @@ func (_c *Repos_Update_Call) Run(run func(ctx context.Context, p domain.Principa
401401 return _c
402402 }
403403
404-func (_c *Repos_Update_Call) Return(repo domain.Repo, err error) *Repos_Update_Call {
404+func (_c *RepoService_Update_Call) Return(repo domain.Repo, err error) *RepoService_Update_Call {
405405 _c.Call.Return(repo, err)
406406 return _c
407407 }
408408
409-func (_c *Repos_Update_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, params app.UpdateParams) (domain.Repo, error)) *Repos_Update_Call {
409+func (_c *RepoService_Update_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, name domain.RepoName, params ports.UpdateRepoParams) (domain.Repo, error)) *RepoService_Update_Call {
410410 _c.Call.Return(run)
411411 return _c
412412 }
413413
414-// View provides a mock function for the type Repos
415-func (_mock *Repos) View(ctx context.Context, repo domain.Repo) (app.RepoView, error) {
414+// View provides a mock function for the type RepoService
415+func (_mock *RepoService) View(ctx context.Context, repo domain.Repo) (ports.RepoView, error) {
416416 ret := _mock.Called(ctx, repo)
417417
418418 if len(ret) == 0 {
419419 panic("no return value specified for View")
420420 }
421421
422- var r0 app.RepoView
422+ var r0 ports.RepoView
423423 var r1 error
424- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Repo) (app.RepoView, error)); ok {
424+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Repo) (ports.RepoView, error)); ok {
425425 return returnFunc(ctx, repo)
426426 }
427- if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Repo) app.RepoView); ok {
427+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Repo) ports.RepoView); ok {
428428 r0 = returnFunc(ctx, repo)
429429 } else {
430- r0 = ret.Get(0).(app.RepoView)
430+ r0 = ret.Get(0).(ports.RepoView)
431431 }
432432 if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Repo) error); ok {
433433 r1 = returnFunc(ctx, repo)
@@ -437,19 +437,19 @@ func (_mock *Repos) View(ctx context.Context, repo domain.Repo) (app.RepoView, e
437437 return r0, r1
438438 }
439439
440-// Repos_View_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'View'
441-type Repos_View_Call struct {
440+// RepoService_View_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'View'
441+type RepoService_View_Call struct {
442442 *mock.Call
443443 }
444444
445445 // View is a helper method to define mock.On call
446446 // - ctx context.Context
447447 // - repo domain.Repo
448-func (_e *Repos_Expecter) View(ctx any, repo any) *Repos_View_Call {
449- return &Repos_View_Call{Call: _e.mock.On("View", ctx, repo)}
448+func (_e *RepoService_Expecter) View(ctx any, repo any) *RepoService_View_Call {
449+ return &RepoService_View_Call{Call: _e.mock.On("View", ctx, repo)}
450450 }
451451
452-func (_c *Repos_View_Call) Run(run func(ctx context.Context, repo domain.Repo)) *Repos_View_Call {
452+func (_c *RepoService_View_Call) Run(run func(ctx context.Context, repo domain.Repo)) *RepoService_View_Call {
453453 _c.Call.Run(func(args mock.Arguments) {
454454 var arg0 context.Context
455455 if args[0] != nil {
@@ -467,12 +467,12 @@ func (_c *Repos_View_Call) Run(run func(ctx context.Context, repo domain.Repo))
467467 return _c
468468 }
469469
470-func (_c *Repos_View_Call) Return(repoView app.RepoView, err error) *Repos_View_Call {
470+func (_c *RepoService_View_Call) Return(repoView ports.RepoView, err error) *RepoService_View_Call {
471471 _c.Call.Return(repoView, err)
472472 return _c
473473 }
474474
475-func (_c *Repos_View_Call) RunAndReturn(run func(ctx context.Context, repo domain.Repo) (app.RepoView, error)) *Repos_View_Call {
475+func (_c *RepoService_View_Call) RunAndReturn(run func(ctx context.Context, repo domain.Repo) (ports.RepoView, error)) *RepoService_View_Call {
476476 _c.Call.Return(run)
477477 return _c
478478 }
R internal/app/mocks/Revalidator.go → internal/ports/mocks/Revalidator.go +0 -0 renamed, 100% similar

renamed, 100% similar

A internal/ports/mocks/SessionService.go +172 -0
@@ -0,0 +1,172 @@
1+// Code generated by mockery; DO NOT EDIT.
2+// github.com/vektra/mockery
3+// template: testify
4+
5+package mocks
6+
7+import (
8+ "context"
9+
10+ "bnema.dev/ermite/internal/domain"
11+ "bnema.dev/ermite/internal/ports"
12+ mock "github.com/stretchr/testify/mock"
13+)
14+
15+// NewSessionService creates a new instance of SessionService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
16+// The first argument is typically a *testing.T value.
17+func NewSessionService(t interface {
18+ mock.TestingT
19+ Cleanup(func())
20+}) *SessionService {
21+ if helper, ok := t.(interface{ Helper() }); ok {
22+ helper.Helper()
23+ }
24+
25+ mock := &SessionService{}
26+ mock.Mock.Test(t)
27+
28+ t.Cleanup(func() {
29+ if helper, ok := t.(interface{ Helper() }); ok {
30+ helper.Helper()
31+ }
32+ mock.AssertExpectations(t)
33+ })
34+
35+ return mock
36+}
37+
38+// SessionService is an autogenerated mock type for the SessionService type
39+type SessionService struct {
40+ mock.Mock
41+}
42+
43+type SessionService_Expecter struct {
44+ mock *mock.Mock
45+}
46+
47+func (_m *SessionService) EXPECT() *SessionService_Expecter {
48+ return &SessionService_Expecter{mock: &_m.Mock}
49+}
50+
51+// CreateLoginLink provides a mock function for the type SessionService
52+func (_mock *SessionService) CreateLoginLink(ctx context.Context, p domain.Principal) (ports.LoginLink, error) {
53+ ret := _mock.Called(ctx, p)
54+
55+ if len(ret) == 0 {
56+ panic("no return value specified for CreateLoginLink")
57+ }
58+
59+ var r0 ports.LoginLink
60+ var r1 error
61+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) (ports.LoginLink, error)); ok {
62+ return returnFunc(ctx, p)
63+ }
64+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) ports.LoginLink); ok {
65+ r0 = returnFunc(ctx, p)
66+ } else {
67+ r0 = ret.Get(0).(ports.LoginLink)
68+ }
69+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal) error); ok {
70+ r1 = returnFunc(ctx, p)
71+ } else {
72+ r1 = ret.Error(1)
73+ }
74+ return r0, r1
75+}
76+
77+// SessionService_CreateLoginLink_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateLoginLink'
78+type SessionService_CreateLoginLink_Call struct {
79+ *mock.Call
80+}
81+
82+// CreateLoginLink is a helper method to define mock.On call
83+// - ctx context.Context
84+// - p domain.Principal
85+func (_e *SessionService_Expecter) CreateLoginLink(ctx any, p any) *SessionService_CreateLoginLink_Call {
86+ return &SessionService_CreateLoginLink_Call{Call: _e.mock.On("CreateLoginLink", ctx, p)}
87+}
88+
89+func (_c *SessionService_CreateLoginLink_Call) Run(run func(ctx context.Context, p domain.Principal)) *SessionService_CreateLoginLink_Call {
90+ _c.Call.Run(func(args mock.Arguments) {
91+ var arg0 context.Context
92+ if args[0] != nil {
93+ arg0 = args[0].(context.Context)
94+ }
95+ var arg1 domain.Principal
96+ if args[1] != nil {
97+ arg1 = args[1].(domain.Principal)
98+ }
99+ run(
100+ arg0,
101+ arg1,
102+ )
103+ })
104+ return _c
105+}
106+
107+func (_c *SessionService_CreateLoginLink_Call) Return(loginLink ports.LoginLink, err error) *SessionService_CreateLoginLink_Call {
108+ _c.Call.Return(loginLink, err)
109+ return _c
110+}
111+
112+func (_c *SessionService_CreateLoginLink_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal) (ports.LoginLink, error)) *SessionService_CreateLoginLink_Call {
113+ _c.Call.Return(run)
114+ return _c
115+}
116+
117+// SignOutEverywhere provides a mock function for the type SessionService
118+func (_mock *SessionService) SignOutEverywhere(ctx context.Context, p domain.Principal) error {
119+ ret := _mock.Called(ctx, p)
120+
121+ if len(ret) == 0 {
122+ panic("no return value specified for SignOutEverywhere")
123+ }
124+
125+ var r0 error
126+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) error); ok {
127+ r0 = returnFunc(ctx, p)
128+ } else {
129+ r0 = ret.Error(0)
130+ }
131+ return r0
132+}
133+
134+// SessionService_SignOutEverywhere_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SignOutEverywhere'
135+type SessionService_SignOutEverywhere_Call struct {
136+ *mock.Call
137+}
138+
139+// SignOutEverywhere is a helper method to define mock.On call
140+// - ctx context.Context
141+// - p domain.Principal
142+func (_e *SessionService_Expecter) SignOutEverywhere(ctx any, p any) *SessionService_SignOutEverywhere_Call {
143+ return &SessionService_SignOutEverywhere_Call{Call: _e.mock.On("SignOutEverywhere", ctx, p)}
144+}
145+
146+func (_c *SessionService_SignOutEverywhere_Call) Run(run func(ctx context.Context, p domain.Principal)) *SessionService_SignOutEverywhere_Call {
147+ _c.Call.Run(func(args mock.Arguments) {
148+ var arg0 context.Context
149+ if args[0] != nil {
150+ arg0 = args[0].(context.Context)
151+ }
152+ var arg1 domain.Principal
153+ if args[1] != nil {
154+ arg1 = args[1].(domain.Principal)
155+ }
156+ run(
157+ arg0,
158+ arg1,
159+ )
160+ })
161+ return _c
162+}
163+
164+func (_c *SessionService_SignOutEverywhere_Call) Return(err error) *SessionService_SignOutEverywhere_Call {
165+ _c.Call.Return(err)
166+ return _c
167+}
168+
169+func (_c *SessionService_SignOutEverywhere_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal) error) *SessionService_SignOutEverywhere_Call {
170+ _c.Call.Return(run)
171+ return _c
172+}
A internal/ports/mocks/SigningKeyService.go +90 -0
@@ -0,0 +1,90 @@
1+// Code generated by mockery; DO NOT EDIT.
2+// github.com/vektra/mockery
3+// template: testify
4+
5+package mocks
6+
7+import (
8+ "bnema.dev/ermite/internal/domain"
9+ mock "github.com/stretchr/testify/mock"
10+)
11+
12+// NewSigningKeyService creates a new instance of SigningKeyService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
13+// The first argument is typically a *testing.T value.
14+func NewSigningKeyService(t interface {
15+ mock.TestingT
16+ Cleanup(func())
17+}) *SigningKeyService {
18+ if helper, ok := t.(interface{ Helper() }); ok {
19+ helper.Helper()
20+ }
21+
22+ mock := &SigningKeyService{}
23+ mock.Mock.Test(t)
24+
25+ t.Cleanup(func() {
26+ if helper, ok := t.(interface{ Helper() }); ok {
27+ helper.Helper()
28+ }
29+ mock.AssertExpectations(t)
30+ })
31+
32+ return mock
33+}
34+
35+// SigningKeyService is an autogenerated mock type for the SigningKeyService type
36+type SigningKeyService struct {
37+ mock.Mock
38+}
39+
40+type SigningKeyService_Expecter struct {
41+ mock *mock.Mock
42+}
43+
44+func (_m *SigningKeyService) EXPECT() *SigningKeyService_Expecter {
45+ return &SigningKeyService_Expecter{mock: &_m.Mock}
46+}
47+
48+// Info provides a mock function for the type SigningKeyService
49+func (_mock *SigningKeyService) Info() domain.SigningKeyInfo {
50+ ret := _mock.Called()
51+
52+ if len(ret) == 0 {
53+ panic("no return value specified for Info")
54+ }
55+
56+ var r0 domain.SigningKeyInfo
57+ if returnFunc, ok := ret.Get(0).(func() domain.SigningKeyInfo); ok {
58+ r0 = returnFunc()
59+ } else {
60+ r0 = ret.Get(0).(domain.SigningKeyInfo)
61+ }
62+ return r0
63+}
64+
65+// SigningKeyService_Info_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Info'
66+type SigningKeyService_Info_Call struct {
67+ *mock.Call
68+}
69+
70+// Info is a helper method to define mock.On call
71+func (_e *SigningKeyService_Expecter) Info() *SigningKeyService_Info_Call {
72+ return &SigningKeyService_Info_Call{Call: _e.mock.On("Info")}
73+}
74+
75+func (_c *SigningKeyService_Info_Call) Run(run func()) *SigningKeyService_Info_Call {
76+ _c.Call.Run(func(args mock.Arguments) {
77+ run()
78+ })
79+ return _c
80+}
81+
82+func (_c *SigningKeyService_Info_Call) Return(signingKeyInfo domain.SigningKeyInfo) *SigningKeyService_Info_Call {
83+ _c.Call.Return(signingKeyInfo)
84+ return _c
85+}
86+
87+func (_c *SigningKeyService_Info_Call) RunAndReturn(run func() domain.SigningKeyInfo) *SigningKeyService_Info_Call {
88+ _c.Call.Return(run)
89+ return _c
90+}
A internal/ports/mocks/TokenService.go +262 -0
@@ -0,0 +1,262 @@
1+// Code generated by mockery; DO NOT EDIT.
2+// github.com/vektra/mockery
3+// template: testify
4+
5+package mocks
6+
7+import (
8+ "context"
9+
10+ "bnema.dev/ermite/internal/domain"
11+ "bnema.dev/ermite/internal/ports"
12+ mock "github.com/stretchr/testify/mock"
13+)
14+
15+// NewTokenService creates a new instance of TokenService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
16+// The first argument is typically a *testing.T value.
17+func NewTokenService(t interface {
18+ mock.TestingT
19+ Cleanup(func())
20+}) *TokenService {
21+ if helper, ok := t.(interface{ Helper() }); ok {
22+ helper.Helper()
23+ }
24+
25+ mock := &TokenService{}
26+ mock.Mock.Test(t)
27+
28+ t.Cleanup(func() {
29+ if helper, ok := t.(interface{ Helper() }); ok {
30+ helper.Helper()
31+ }
32+ mock.AssertExpectations(t)
33+ })
34+
35+ return mock
36+}
37+
38+// TokenService is an autogenerated mock type for the TokenService type
39+type TokenService struct {
40+ mock.Mock
41+}
42+
43+type TokenService_Expecter struct {
44+ mock *mock.Mock
45+}
46+
47+func (_m *TokenService) EXPECT() *TokenService_Expecter {
48+ return &TokenService_Expecter{mock: &_m.Mock}
49+}
50+
51+// Create provides a mock function for the type TokenService
52+func (_mock *TokenService) Create(ctx context.Context, p domain.Principal, params ports.CreateTokenParams) (ports.CreatedToken, error) {
53+ ret := _mock.Called(ctx, p, params)
54+
55+ if len(ret) == 0 {
56+ panic("no return value specified for Create")
57+ }
58+
59+ var r0 ports.CreatedToken
60+ var r1 error
61+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, ports.CreateTokenParams) (ports.CreatedToken, error)); ok {
62+ return returnFunc(ctx, p, params)
63+ }
64+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, ports.CreateTokenParams) ports.CreatedToken); ok {
65+ r0 = returnFunc(ctx, p, params)
66+ } else {
67+ r0 = ret.Get(0).(ports.CreatedToken)
68+ }
69+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, ports.CreateTokenParams) error); ok {
70+ r1 = returnFunc(ctx, p, params)
71+ } else {
72+ r1 = ret.Error(1)
73+ }
74+ return r0, r1
75+}
76+
77+// TokenService_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create'
78+type TokenService_Create_Call struct {
79+ *mock.Call
80+}
81+
82+// Create is a helper method to define mock.On call
83+// - ctx context.Context
84+// - p domain.Principal
85+// - params ports.CreateTokenParams
86+func (_e *TokenService_Expecter) Create(ctx any, p any, params any) *TokenService_Create_Call {
87+ return &TokenService_Create_Call{Call: _e.mock.On("Create", ctx, p, params)}
88+}
89+
90+func (_c *TokenService_Create_Call) Run(run func(ctx context.Context, p domain.Principal, params ports.CreateTokenParams)) *TokenService_Create_Call {
91+ _c.Call.Run(func(args mock.Arguments) {
92+ var arg0 context.Context
93+ if args[0] != nil {
94+ arg0 = args[0].(context.Context)
95+ }
96+ var arg1 domain.Principal
97+ if args[1] != nil {
98+ arg1 = args[1].(domain.Principal)
99+ }
100+ var arg2 ports.CreateTokenParams
101+ if args[2] != nil {
102+ arg2 = args[2].(ports.CreateTokenParams)
103+ }
104+ run(
105+ arg0,
106+ arg1,
107+ arg2,
108+ )
109+ })
110+ return _c
111+}
112+
113+func (_c *TokenService_Create_Call) Return(createdToken ports.CreatedToken, err error) *TokenService_Create_Call {
114+ _c.Call.Return(createdToken, err)
115+ return _c
116+}
117+
118+func (_c *TokenService_Create_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, params ports.CreateTokenParams) (ports.CreatedToken, error)) *TokenService_Create_Call {
119+ _c.Call.Return(run)
120+ return _c
121+}
122+
123+// ListPage provides a mock function for the type TokenService
124+func (_mock *TokenService) ListPage(ctx context.Context, p domain.Principal, size int, token string) (ports.TokenPage, error) {
125+ ret := _mock.Called(ctx, p, size, token)
126+
127+ if len(ret) == 0 {
128+ panic("no return value specified for ListPage")
129+ }
130+
131+ var r0 ports.TokenPage
132+ var r1 error
133+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, int, string) (ports.TokenPage, error)); ok {
134+ return returnFunc(ctx, p, size, token)
135+ }
136+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, int, string) ports.TokenPage); ok {
137+ r0 = returnFunc(ctx, p, size, token)
138+ } else {
139+ r0 = ret.Get(0).(ports.TokenPage)
140+ }
141+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, int, string) error); ok {
142+ r1 = returnFunc(ctx, p, size, token)
143+ } else {
144+ r1 = ret.Error(1)
145+ }
146+ return r0, r1
147+}
148+
149+// TokenService_ListPage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListPage'
150+type TokenService_ListPage_Call struct {
151+ *mock.Call
152+}
153+
154+// ListPage is a helper method to define mock.On call
155+// - ctx context.Context
156+// - p domain.Principal
157+// - size int
158+// - token string
159+func (_e *TokenService_Expecter) ListPage(ctx any, p any, size any, token any) *TokenService_ListPage_Call {
160+ return &TokenService_ListPage_Call{Call: _e.mock.On("ListPage", ctx, p, size, token)}
161+}
162+
163+func (_c *TokenService_ListPage_Call) Run(run func(ctx context.Context, p domain.Principal, size int, token string)) *TokenService_ListPage_Call {
164+ _c.Call.Run(func(args mock.Arguments) {
165+ var arg0 context.Context
166+ if args[0] != nil {
167+ arg0 = args[0].(context.Context)
168+ }
169+ var arg1 domain.Principal
170+ if args[1] != nil {
171+ arg1 = args[1].(domain.Principal)
172+ }
173+ var arg2 int
174+ if args[2] != nil {
175+ arg2 = args[2].(int)
176+ }
177+ var arg3 string
178+ if args[3] != nil {
179+ arg3 = args[3].(string)
180+ }
181+ run(
182+ arg0,
183+ arg1,
184+ arg2,
185+ arg3,
186+ )
187+ })
188+ return _c
189+}
190+
191+func (_c *TokenService_ListPage_Call) Return(tokenPage ports.TokenPage, err error) *TokenService_ListPage_Call {
192+ _c.Call.Return(tokenPage, err)
193+ return _c
194+}
195+
196+func (_c *TokenService_ListPage_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, size int, token string) (ports.TokenPage, error)) *TokenService_ListPage_Call {
197+ _c.Call.Return(run)
198+ return _c
199+}
200+
201+// Revoke provides a mock function for the type TokenService
202+func (_mock *TokenService) Revoke(ctx context.Context, p domain.Principal, id string) error {
203+ ret := _mock.Called(ctx, p, id)
204+
205+ if len(ret) == 0 {
206+ panic("no return value specified for Revoke")
207+ }
208+
209+ var r0 error
210+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, string) error); ok {
211+ r0 = returnFunc(ctx, p, id)
212+ } else {
213+ r0 = ret.Error(0)
214+ }
215+ return r0
216+}
217+
218+// TokenService_Revoke_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Revoke'
219+type TokenService_Revoke_Call struct {
220+ *mock.Call
221+}
222+
223+// Revoke is a helper method to define mock.On call
224+// - ctx context.Context
225+// - p domain.Principal
226+// - id string
227+func (_e *TokenService_Expecter) Revoke(ctx any, p any, id any) *TokenService_Revoke_Call {
228+ return &TokenService_Revoke_Call{Call: _e.mock.On("Revoke", ctx, p, id)}
229+}
230+
231+func (_c *TokenService_Revoke_Call) Run(run func(ctx context.Context, p domain.Principal, id string)) *TokenService_Revoke_Call {
232+ _c.Call.Run(func(args mock.Arguments) {
233+ var arg0 context.Context
234+ if args[0] != nil {
235+ arg0 = args[0].(context.Context)
236+ }
237+ var arg1 domain.Principal
238+ if args[1] != nil {
239+ arg1 = args[1].(domain.Principal)
240+ }
241+ var arg2 string
242+ if args[2] != nil {
243+ arg2 = args[2].(string)
244+ }
245+ run(
246+ arg0,
247+ arg1,
248+ arg2,
249+ )
250+ })
251+ return _c
252+}
253+
254+func (_c *TokenService_Revoke_Call) Return(err error) *TokenService_Revoke_Call {
255+ _c.Call.Return(err)
256+ return _c
257+}
258+
259+func (_c *TokenService_Revoke_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, id string) error) *TokenService_Revoke_Call {
260+ _c.Call.Return(run)
261+ return _c
262+}
R internal/adapters/web/mocks/SessionAuth.go → internal/ports/mocks/WebSessionService.go +36 -36 renamed, 52% similar
@@ -11,17 +11,17 @@ import (
1111 mock "github.com/stretchr/testify/mock"
1212 )
1313
14-// NewSessionAuth creates a new instance of SessionAuth. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
14+// NewWebSessionService creates a new instance of WebSessionService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
1515 // The first argument is typically a *testing.T value.
16-func NewSessionAuth(t interface {
16+func NewWebSessionService(t interface {
1717 mock.TestingT
1818 Cleanup(func())
19-}) *SessionAuth {
19+}) *WebSessionService {
2020 if helper, ok := t.(interface{ Helper() }); ok {
2121 helper.Helper()
2222 }
2323
24- mock := &SessionAuth{}
24+ mock := &WebSessionService{}
2525 mock.Mock.Test(t)
2626
2727 t.Cleanup(func() {
@@ -34,21 +34,21 @@ func NewSessionAuth(t interface {
3434 return mock
3535 }
3636
37-// SessionAuth is an autogenerated mock type for the SessionAuth type
38-type SessionAuth struct {
37+// WebSessionService is an autogenerated mock type for the WebSessionService type
38+type WebSessionService struct {
3939 mock.Mock
4040 }
4141
42-type SessionAuth_Expecter struct {
42+type WebSessionService_Expecter struct {
4343 mock *mock.Mock
4444 }
4545
46-func (_m *SessionAuth) EXPECT() *SessionAuth_Expecter {
47- return &SessionAuth_Expecter{mock: &_m.Mock}
46+func (_m *WebSessionService) EXPECT() *WebSessionService_Expecter {
47+ return &WebSessionService_Expecter{mock: &_m.Mock}
4848 }
4949
50-// Login provides a mock function for the type SessionAuth
51-func (_mock *SessionAuth) Login(ctx context.Context, nonce string) (string, time.Duration, error) {
50+// Login provides a mock function for the type WebSessionService
51+func (_mock *WebSessionService) Login(ctx context.Context, nonce string) (string, time.Duration, error) {
5252 ret := _mock.Called(ctx, nonce)
5353
5454 if len(ret) == 0 {
@@ -79,19 +79,19 @@ func (_mock *SessionAuth) Login(ctx context.Context, nonce string) (string, time
7979 return r0, r1, r2
8080 }
8181
82-// SessionAuth_Login_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Login'
83-type SessionAuth_Login_Call struct {
82+// WebSessionService_Login_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Login'
83+type WebSessionService_Login_Call struct {
8484 *mock.Call
8585 }
8686
8787 // Login is a helper method to define mock.On call
8888 // - ctx context.Context
8989 // - nonce string
90-func (_e *SessionAuth_Expecter) Login(ctx any, nonce any) *SessionAuth_Login_Call {
91- return &SessionAuth_Login_Call{Call: _e.mock.On("Login", ctx, nonce)}
90+func (_e *WebSessionService_Expecter) Login(ctx any, nonce any) *WebSessionService_Login_Call {
91+ return &WebSessionService_Login_Call{Call: _e.mock.On("Login", ctx, nonce)}
9292 }
9393
94-func (_c *SessionAuth_Login_Call) Run(run func(ctx context.Context, nonce string)) *SessionAuth_Login_Call {
94+func (_c *WebSessionService_Login_Call) Run(run func(ctx context.Context, nonce string)) *WebSessionService_Login_Call {
9595 _c.Call.Run(func(args mock.Arguments) {
9696 var arg0 context.Context
9797 if args[0] != nil {
@@ -109,18 +109,18 @@ func (_c *SessionAuth_Login_Call) Run(run func(ctx context.Context, nonce string
109109 return _c
110110 }
111111
112-func (_c *SessionAuth_Login_Call) Return(token string, lifetime time.Duration, err error) *SessionAuth_Login_Call {
112+func (_c *WebSessionService_Login_Call) Return(token string, lifetime time.Duration, err error) *WebSessionService_Login_Call {
113113 _c.Call.Return(token, lifetime, err)
114114 return _c
115115 }
116116
117-func (_c *SessionAuth_Login_Call) RunAndReturn(run func(ctx context.Context, nonce string) (string, time.Duration, error)) *SessionAuth_Login_Call {
117+func (_c *WebSessionService_Login_Call) RunAndReturn(run func(ctx context.Context, nonce string) (string, time.Duration, error)) *WebSessionService_Login_Call {
118118 _c.Call.Return(run)
119119 return _c
120120 }
121121
122-// LoginLinkValid provides a mock function for the type SessionAuth
123-func (_mock *SessionAuth) LoginLinkValid(nonce string) bool {
122+// LoginLinkValid provides a mock function for the type WebSessionService
123+func (_mock *WebSessionService) LoginLinkValid(nonce string) bool {
124124 ret := _mock.Called(nonce)
125125
126126 if len(ret) == 0 {
@@ -136,18 +136,18 @@ func (_mock *SessionAuth) LoginLinkValid(nonce string) bool {
136136 return r0
137137 }
138138
139-// SessionAuth_LoginLinkValid_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LoginLinkValid'
140-type SessionAuth_LoginLinkValid_Call struct {
139+// WebSessionService_LoginLinkValid_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LoginLinkValid'
140+type WebSessionService_LoginLinkValid_Call struct {
141141 *mock.Call
142142 }
143143
144144 // LoginLinkValid is a helper method to define mock.On call
145145 // - nonce string
146-func (_e *SessionAuth_Expecter) LoginLinkValid(nonce any) *SessionAuth_LoginLinkValid_Call {
147- return &SessionAuth_LoginLinkValid_Call{Call: _e.mock.On("LoginLinkValid", nonce)}
146+func (_e *WebSessionService_Expecter) LoginLinkValid(nonce any) *WebSessionService_LoginLinkValid_Call {
147+ return &WebSessionService_LoginLinkValid_Call{Call: _e.mock.On("LoginLinkValid", nonce)}
148148 }
149149
150-func (_c *SessionAuth_LoginLinkValid_Call) Run(run func(nonce string)) *SessionAuth_LoginLinkValid_Call {
150+func (_c *WebSessionService_LoginLinkValid_Call) Run(run func(nonce string)) *WebSessionService_LoginLinkValid_Call {
151151 _c.Call.Run(func(args mock.Arguments) {
152152 var arg0 string
153153 if args[0] != nil {
@@ -160,18 +160,18 @@ func (_c *SessionAuth_LoginLinkValid_Call) Run(run func(nonce string)) *SessionA
160160 return _c
161161 }
162162
163-func (_c *SessionAuth_LoginLinkValid_Call) Return(b bool) *SessionAuth_LoginLinkValid_Call {
163+func (_c *WebSessionService_LoginLinkValid_Call) Return(b bool) *WebSessionService_LoginLinkValid_Call {
164164 _c.Call.Return(b)
165165 return _c
166166 }
167167
168-func (_c *SessionAuth_LoginLinkValid_Call) RunAndReturn(run func(nonce string) bool) *SessionAuth_LoginLinkValid_Call {
168+func (_c *WebSessionService_LoginLinkValid_Call) RunAndReturn(run func(nonce string) bool) *WebSessionService_LoginLinkValid_Call {
169169 _c.Call.Return(run)
170170 return _c
171171 }
172172
173-// Verify provides a mock function for the type SessionAuth
174-func (_mock *SessionAuth) Verify(ctx context.Context, token string) (context.Context, bool, error) {
173+// Verify provides a mock function for the type WebSessionService
174+func (_mock *WebSessionService) Verify(ctx context.Context, token string) (context.Context, bool, error) {
175175 ret := _mock.Called(ctx, token)
176176
177177 if len(ret) == 0 {
@@ -204,19 +204,19 @@ func (_mock *SessionAuth) Verify(ctx context.Context, token string) (context.Con
204204 return r0, r1, r2
205205 }
206206
207-// SessionAuth_Verify_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Verify'
208-type SessionAuth_Verify_Call struct {
207+// WebSessionService_Verify_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Verify'
208+type WebSessionService_Verify_Call struct {
209209 *mock.Call
210210 }
211211
212212 // Verify is a helper method to define mock.On call
213213 // - ctx context.Context
214214 // - token string
215-func (_e *SessionAuth_Expecter) Verify(ctx any, token any) *SessionAuth_Verify_Call {
216- return &SessionAuth_Verify_Call{Call: _e.mock.On("Verify", ctx, token)}
215+func (_e *WebSessionService_Expecter) Verify(ctx any, token any) *WebSessionService_Verify_Call {
216+ return &WebSessionService_Verify_Call{Call: _e.mock.On("Verify", ctx, token)}
217217 }
218218
219-func (_c *SessionAuth_Verify_Call) Run(run func(ctx context.Context, token string)) *SessionAuth_Verify_Call {
219+func (_c *WebSessionService_Verify_Call) Run(run func(ctx context.Context, token string)) *WebSessionService_Verify_Call {
220220 _c.Call.Run(func(args mock.Arguments) {
221221 var arg0 context.Context
222222 if args[0] != nil {
@@ -234,12 +234,12 @@ func (_c *SessionAuth_Verify_Call) Run(run func(ctx context.Context, token strin
234234 return _c
235235 }
236236
237-func (_c *SessionAuth_Verify_Call) Return(owner context.Context, ok bool, err error) *SessionAuth_Verify_Call {
237+func (_c *WebSessionService_Verify_Call) Return(owner context.Context, ok bool, err error) *WebSessionService_Verify_Call {
238238 _c.Call.Return(owner, ok, err)
239239 return _c
240240 }
241241
242-func (_c *SessionAuth_Verify_Call) RunAndReturn(run func(ctx context.Context, token string) (context.Context, bool, error)) *SessionAuth_Verify_Call {
242+func (_c *WebSessionService_Verify_Call) RunAndReturn(run func(ctx context.Context, token string) (context.Context, bool, error)) *WebSessionService_Verify_Call {
243243 _c.Call.Return(run)
244244 return _c
245245 }
M internal/ports/ports.go +9 -2
@@ -1,7 +1,14 @@
11 // SPDX-License-Identifier: AGPL-3.0-or-later
22
3-// Package ports declares the interfaces the use cases need from the outside
4-// world. Adapters implement them; cmd wires them.
3+// Package ports declares every interface that crosses a layer, and the
4+// parameter and result types of the use case interfaces.
5+//
6+// Outbound ports (ports.go) are what the use cases need from the outside
7+// world: adapters implement them. Inbound ports (services.go) are the use
8+// cases as the adapters that serve requests see them: the types of package app
9+// implement them. Adapters and app depend on this package and on domain, never
10+// on each other; cmd wires the implementations. All the mocks are generated in
11+// ports/mocks.
512 package ports
613
714 import (
A internal/ports/services.go +162 -0
@@ -0,0 +1,162 @@
1+// SPDX-License-Identifier: AGPL-3.0-or-later
2+
3+package ports
4+
5+import (
6+ "context"
7+ "errors"
8+ "io"
9+ "time"
10+
11+ "bnema.dev/ermite/internal/domain"
12+)
13+
14+// The interfaces below are the use cases as the inbound adapters (the API,
15+// Git over HTTP, the web) and the entrypoint see them. The types of package
16+// app implement them, each assertion being made in app; the mocks in
17+// ports/mocks stand in for them in the tests of the adapters.
18+
19+// AuthService turns the secret of a request into a principal. It returns
20+// domain.ErrUnauthenticated for a secret that is not valid. *app.Auth
21+// implements it.
22+type AuthService interface {
23+ Authenticate(ctx context.Context, secret string) (domain.Principal, error)
24+}
25+
26+// Revalidator confirms that a principal obtained earlier is still
27+// authorized. *app.Auth implements it.
28+type Revalidator interface {
29+ Revalidate(ctx context.Context, p domain.Principal) error
30+}
31+
32+// RepoLookup is the read side of a single repository. A repository the
33+// principal may not read is domain.ErrNotFound. *app.Repos implements it, as
34+// part of RepoService.
35+type RepoLookup interface {
36+ Get(ctx context.Context, p domain.Principal, name domain.RepoName) (domain.Repo, error)
37+}
38+
39+// RepoService is the repository use cases the API serves. *app.Repos
40+// implements it.
41+type RepoService interface {
42+ RepoLookup
43+ ListPage(ctx context.Context, p domain.Principal, size int, token string) (RepoPage, error)
44+ View(ctx context.Context, repo domain.Repo) (RepoView, error)
45+ Create(ctx context.Context, p domain.Principal, params CreateRepoParams) (domain.Repo, error)
46+ Update(ctx context.Context, p domain.Principal, name domain.RepoName, params UpdateRepoParams) (domain.Repo, error)
47+ Delete(ctx context.Context, p domain.Principal, name domain.RepoName) error
48+}
49+
50+// TokenService is the access token use cases the API serves. *app.Tokens
51+// implements it.
52+type TokenService interface {
53+ Create(ctx context.Context, p domain.Principal, params CreateTokenParams) (CreatedToken, error)
54+ ListPage(ctx context.Context, p domain.Principal, size int, token string) (TokenPage, error)
55+ Revoke(ctx context.Context, p domain.Principal, id string) error
56+}
57+
58+// BrowseService is the read side of a repository's content. *app.Browse
59+// implements it.
60+type BrowseService interface {
61+ ListRefs(ctx context.Context, p domain.Principal, repo domain.RepoName, params ListRefsParams) (RefPage, error)
62+ ResolveRevision(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision) (domain.ResolvedRevision, error)
63+ GetTree(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision, path domain.TreePath, includeLastCommit bool) (domain.Tree, error)
64+ OpenBlob(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision, path domain.TreePath) (domain.BlobInfo, io.ReadCloser, error)
65+ ListCommits(ctx context.Context, p domain.Principal, repo domain.RepoName, params ListCommitsParams) (HistoryPage, error)
66+ GetCommit(ctx context.Context, p domain.Principal, repo domain.RepoName, rev domain.Revision) (domain.CommitDetail, error)
67+}
68+
69+// EventService streams the events of the forge. *app.Events implements it.
70+type EventService interface {
71+ Watch(ctx context.Context, p domain.Principal, params WatchParams, send func(WatchEvent) error) error
72+}
73+
74+// MergeRequestService is the merge request use cases. *app.MergeRequests
75+// implements it.
76+type MergeRequestService interface {
77+ Create(ctx context.Context, p domain.Principal, name domain.RepoName, params CreateMergeRequestParams) (domain.MergeRequest, error)
78+ List(ctx context.Context, p domain.Principal, name domain.RepoName, params ListMergeRequestsParams) (MergeRequestPage, error)
79+ Get(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, sinceLastRead bool) (MergeRequestDetail, error)
80+ AddComment(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params AddCommentParams) (domain.Comment, error)
81+ ResolveComment(ctx context.Context, p domain.Principal, name domain.RepoName, number, commentID int64, resolved bool) (domain.Comment, error)
82+ Close(ctx context.Context, p domain.Principal, name domain.RepoName, number int64) (domain.MergeRequest, error)
83+ MarkRead(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, head domain.ObjectID) (domain.ObjectID, error)
84+ Merge(ctx context.Context, p domain.Principal, name domain.RepoName, number int64, params MergeParams) (MergeResult, error)
85+}
86+
87+// SigningKeyService tells which key signs the commits of the forge.
88+// *app.SigningKey implements it.
89+type SigningKeyService interface {
90+ Info() domain.SigningKeyInfo
91+}
92+
93+// SessionService is the owner's web session use cases the API serves.
94+// *app.Sessions implements it.
95+type SessionService interface {
96+ CreateLoginLink(ctx context.Context, p domain.Principal) (LoginLink, error)
97+ SignOutEverywhere(ctx context.Context, p domain.Principal) error
98+}
99+
100+// ErrLinkRejected is what WebSessionService.Login returns for a link that is
101+// unknown, expired or already used.
102+var ErrLinkRejected = errors.New("login link rejected")
103+
104+// WebSessionService is the session side of the use cases, as the web needs
105+// it. The entrypoint implements it over *app.Sessions.
106+type WebSessionService interface {
107+ // LoginLinkValid reports whether the link is pending, without using it.
108+ LoginLinkValid(nonce string) bool
109+ // Login uses the link and returns the session token and how long it
110+ // lasts, or ErrLinkRejected.
111+ Login(ctx context.Context, nonce string) (token string, lifetime time.Duration, err error)
112+ // Verify checks a session token. When it is valid it returns the context
113+ // to serve the request with, which carries the owner as the caller of the
114+ // API; when it is not, ok is false and err nil. An error means the check
115+ // could not be made (the request then runs as anonymous).
116+ Verify(ctx context.Context, token string) (owner context.Context, ok bool, err error)
117+}
118+
119+// PushService opens a repository for a push: it is domain.ErrUnauthenticated
120+// for an anonymous caller, domain.ErrNotFound for a repository the caller may
121+// not write and cannot see (or that is missing and may not be created),
122+// domain.ErrForbidden for one it can see but not write. With create, a
123+// missing repository the principal may write is created. *app.Pushes
124+// implements it.
125+type PushService interface {
126+ Open(ctx context.Context, p domain.Principal, name domain.RepoName, create bool) error
127+}
128+
129+// PushHookService is what the hook socket asks of the use cases.
130+// *app.Pushes implements it.
131+type PushHookService interface {
132+ // Check decides a push before Git applies it; an error matching
133+ // domain.ErrRejected refuses it, any other error refuses it as a failure.
134+ Check(ctx context.Context, p domain.Principal, name domain.RepoName, updates []domain.RefUpdate) error
135+ // Record handles an accepted push.
136+ Record(ctx context.Context, p domain.Principal, name domain.RepoName, updates []domain.RefUpdate) error
137+}
138+
139+// MaintenanceScheduler queues repository maintenance. *app.Maintainer
140+// implements it.
141+type MaintenanceScheduler interface {
142+ Schedule(ctx context.Context, name domain.RepoName)
143+}
144+
145+// HookTokens hands out the per-request tokens the hooks of a push present
146+// back to ermite, and the socket they present them on. The Git HTTP adapter
147+// implements it.
148+type HookTokens interface {
149+ // Register binds a new token to the repository and principal of a push.
150+ // release revokes it and must be called when the request ends.
151+ Register(ctx context.Context, repo domain.RepoName, p domain.Principal) (token string, release func())
152+ // Socket is the path of the hook socket.
153+ Socket() string
154+}
155+
156+// FailureLimiter counts the failed authentications per client. The adapters
157+// that authenticate share one, so that a client has one budget of failures.
158+// *clientlimit.Limiter implements it.
159+type FailureLimiter interface {
160+ Blocked(ctx context.Context, addr string) (bool, time.Duration)
161+ Fail(ctx context.Context, addr string)
162+}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 2

Tags 43

↑↓ moveenter openesc close