detached 1069a88

feat(domain): add push mirror config, state machine, ref selection and ports

commit
1069a88a28a07ffffe57a92bc201366cad515f91
author
brice · · 2026-10-20 05:29 UTC
committer
brice · · 2026-10-20 10:02 UTC
parent
55fcc8f
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
14 files · +3086 -0
M internal/domain/event.go +23 -0
@@ -304,3 +304,26 @@ type EventGap struct{}
304304
305305 // EventKind implements Event.
306306 func (EventGap) EventKind() string { return "gap" }
307+
308+// MirrorFailed is published when a mirror enters failing or paused (transitions only).
309+type MirrorFailed struct {
310+ Repo RepoName
311+ State MirrorState
312+ Class MirrorFailureClass
313+ Reason string
314+ At time.Time
315+ Visibility Visibility
316+ CreatedAt time.Time
317+}
318+
319+func (MirrorFailed) EventKind() string { return "mirror.failed" }
320+
321+// MirrorRecovered is published when a failing or paused mirror succeeds again.
322+type MirrorRecovered struct {
323+ Repo RepoName
324+ At time.Time
325+ Visibility Visibility
326+ CreatedAt time.Time
327+}
328+
329+func (MirrorRecovered) EventKind() string { return "mirror.recovered" }
@@ -304,3 +304,26 @@ type EventGap struct{}
304 304
305 // EventKind implements Event.305 // EventKind implements Event.
306 func (EventGap) EventKind() string { return "gap" }306 func (EventGap) EventKind() string { return "gap" }
307+
308+// MirrorFailed is published when a mirror enters failing or paused (transitions only).
309+type MirrorFailed struct {
310+ Repo RepoName
311+ State MirrorState
312+ Class MirrorFailureClass
313+ Reason string
314+ At time.Time
315+ Visibility Visibility
316+ CreatedAt time.Time
317+}
318+
319+func (MirrorFailed) EventKind() string { return "mirror.failed" }
320+
321+// MirrorRecovered is published when a failing or paused mirror succeeds again.
322+type MirrorRecovered struct {
323+ Repo RepoName
324+ At time.Time
325+ Visibility Visibility
326+ CreatedAt time.Time
327+}
328+
329+func (MirrorRecovered) EventKind() string { return "mirror.recovered" }
A internal/domain/mirror.go +237 -0
@@ -0,0 +1,237 @@
1+// SPDX-License-Identifier: AGPL-3.0-or-later
2+
3+package domain
4+
5+import (
6+ "fmt"
7+ "net"
8+ "net/url"
9+ "strconv"
10+ "strings"
11+ "time"
12+ "unicode"
13+)
14+
15+const (
16+ MirrorMinInterval = 5 * time.Minute // densest cron allowed
17+ MirrorManualInterval = 60 * time.Second // min gap between manual syncs (not --force)
18+ MirrorRepoPlaceholder = "{repo}"
19+)
20+
21+// MirrorGlobalConfig is the server-wide mirror policy.
22+type MirrorGlobalConfig struct {
23+ Enabled bool
24+ URLTemplate string // ssh URL containing MirrorRepoPlaceholder; "" allowed while disabled
25+ Cron string // 5-field cron, UTC; "" allowed while disabled
26+ AllBranches bool
27+ UpdatedAt time.Time
28+}
29+
30+// MirrorRepoConfig is the per-repository entry: an override (URL != "") or an exclusion.
31+type MirrorRepoConfig struct {
32+ Repo RepoName
33+ URL string // "" means no override
34+ Cron string // "" means inherit global cron
35+ AllBranches *bool // nil means inherit
36+ Excluded bool // opt out of the global template; ignored when URL != ""
37+ UpdatedAt time.Time
38+}
39+type MirrorSource string
40+
41+const (
42+ MirrorSourceNone MirrorSource = "disabled" // nothing applies
43+ MirrorSourceGlobal MirrorSource = "global"
44+ MirrorSourceOverride MirrorSource = "override"
45+ MirrorSourceExcluded MirrorSource = "excluded"
46+)
47+
48+// MirrorTarget is what one repository is mirrored to after resolution.
49+type MirrorTarget struct {
50+ Repo RepoName
51+ Enabled bool
52+ URL string
53+ Cron Cron // zero when !Enabled
54+ CronSpec string
55+ AllBranches bool
56+ Source MirrorSource
57+}
58+
59+func mirrorURLParts(s string) (host, path string, err error) {
60+ bad := func() (string, string, error) {
61+ return "", "", fmt.Errorf("%w: mirror url must be an SSH host and repository path without credentials", ErrInvalid)
62+ }
63+ if strings.ContainsFunc(s, func(r rune) bool { return unicode.IsControl(r) || unicode.IsSpace(r) }) {
64+ return bad()
65+ }
66+ if strings.HasPrefix(s, "ssh://") {
67+ u, e := url.Parse(s)
68+ if e != nil || u.Hostname() == "" || u.RawQuery != "" || u.Fragment != "" {
69+ return bad()
70+ }
71+ if u.User != nil {
72+ if _, ok := u.User.Password(); ok {
73+ return bad()
74+ }
75+ if u.User.Username() == "" {
76+ return bad()
77+ }
78+ }
79+ if p := u.Port(); p != "" {
80+ n, e := strconv.Atoi(p)
81+ if e != nil || n < 1 || n > 65535 {
82+ return bad()
83+ }
84+ }
85+ host = u.Host
86+ path = strings.TrimPrefix(u.Path, "/")
87+ } else {
88+ if strings.Contains(s, "://") {
89+ return bad()
90+ }
91+ var ok bool
92+ host, path, ok = strings.Cut(s, ":")
93+ if !ok {
94+ return bad()
95+ }
96+ if strings.Contains(host, "@") {
97+ user, h, _ := strings.Cut(host, "@")
98+ if user == "" || strings.ContainsAny(user, ":/@") {
99+ return bad()
100+ }
101+ host = h
102+ }
103+ if strings.ContainsAny(host, "/@:") {
104+ return bad()
105+ }
106+ }
107+ if host == "" || strings.HasPrefix(host, "-") || path == "" || strings.HasPrefix(path, "-") || strings.Contains(path, "@") {
108+ return bad()
109+ }
110+ // Reject malformed bracketed addresses as well as empty hosts.
111+ if strings.HasPrefix(host, "[") {
112+ h := strings.TrimSuffix(strings.TrimPrefix(host, "["), "]")
113+ if x, _, e := net.SplitHostPort(host); e == nil {
114+ h = strings.Trim(x, "[]")
115+ }
116+ if net.ParseIP(h) == nil {
117+ return bad()
118+ }
119+ }
120+ return host, path, nil
121+}
122+
123+// ValidateMirrorURL accepts "git@host:path" (scp-like, user optional, no ':' port) and
124+// "ssh://[user@]host[:port]/path". Anything else, an empty host, control chars, or a path
125+// starting with "-" is "%w: mirror url ..." with ErrInvalid. URL is never logged in full
126+// elsewhere: use RedactMirrorURL.
127+func ValidateMirrorURL(s string) error { _, _, err := mirrorURLParts(s); return err }
128+func RedactMirrorURL(s string) string {
129+ h, p, e := mirrorURLParts(s)
130+ if e != nil {
131+ return "[invalid mirror URL]"
132+ }
133+ return h + ":" + p
134+}
135+
136+// ExpandMirrorURL replaces MirrorRepoPlaceholder with repo (as a path segment, not escaped
137+// beyond RepoName's own charset). Returns ErrInvalid when the template has no placeholder.
138+func ExpandMirrorURL(template string, repo RepoName) (string, error) {
139+ if !strings.Contains(template, MirrorRepoPlaceholder) {
140+ return "", fmt.Errorf("%w: mirror url template needs {repo}", ErrInvalid)
141+ }
142+ return strings.ReplaceAll(template, MirrorRepoPlaceholder, repo.String()), nil
143+}
144+func mirrorCron(spec string) (Cron, error) {
145+ c, e := ParseCron(spec)
146+ if e != nil {
147+ return c, e
148+ }
149+ if gap := CronMinGap(c); gap > 0 && gap < MirrorMinInterval {
150+ return Cron{}, fmt.Errorf("%w: cron interval must be at least five minutes", ErrInvalid)
151+ }
152+ return c, nil
153+}
154+
155+// ValidateMirrorGlobalConfig / ValidateMirrorRepoConfig return InvalidFieldsError with fields
156+// "url_template", "cron", "url": cron parsed with ParseCron and checked by CronMinGap >= MirrorMinInterval;
157+// url_template must contain the placeholder; when Enabled both are required.
158+func ValidateMirrorGlobalConfig(g MirrorGlobalConfig) error {
159+ var fields []FieldViolation
160+ if g.Enabled || g.URLTemplate != "" {
161+ repo, _ := ParseRepoName("repo")
162+ u, e := ExpandMirrorURL(g.URLTemplate, repo)
163+ if e == nil {
164+ e = ValidateMirrorURL(u)
165+ }
166+ if e != nil {
167+ fields = append(fields, FieldViolation{"url_template", "must be an SSH URL template containing {repo}"})
168+ }
169+ }
170+ if g.Enabled || g.Cron != "" {
171+ if _, e := mirrorCron(g.Cron); e != nil {
172+ fields = append(fields, FieldViolation{"cron", e.Error()})
173+ }
174+ }
175+ if len(fields) > 0 {
176+ return &InvalidFieldsError{Fields: fields}
177+ }
178+ return nil
179+}
180+func ValidateMirrorRepoConfig(r MirrorRepoConfig) error {
181+ var fields []FieldViolation
182+ if r.URL != "" {
183+ if e := ValidateMirrorURL(r.URL); e != nil {
184+ fields = append(fields, FieldViolation{"url", e.Error()})
185+ }
186+ }
187+ if r.Cron != "" {
188+ if _, e := mirrorCron(r.Cron); e != nil {
189+ fields = append(fields, FieldViolation{"cron", e.Error()})
190+ }
191+ }
192+ if len(fields) > 0 {
193+ return &InvalidFieldsError{Fields: fields}
194+ }
195+ return nil
196+}
197+
198+// ResolveMirror: override wins even when global is disabled; else global, unless excluded.
199+func ResolveMirror(g MirrorGlobalConfig, r *MirrorRepoConfig, repo RepoName) (MirrorTarget, error) {
200+ t := MirrorTarget{Repo: repo, Source: MirrorSourceNone}
201+ if r != nil && r.URL != "" {
202+ t.URL = r.URL
203+ t.Source = MirrorSourceOverride
204+ } else if r != nil && r.Excluded {
205+ t.Source = MirrorSourceExcluded
206+ return t, nil
207+ } else if g.Enabled {
208+ var e error
209+ t.URL, e = ExpandMirrorURL(g.URLTemplate, repo)
210+ if e != nil {
211+ return t, e
212+ }
213+ t.Source = MirrorSourceGlobal
214+ } else {
215+ return t, nil
216+ }
217+ t.CronSpec = g.Cron
218+ t.AllBranches = g.AllBranches
219+ if r != nil {
220+ if r.Cron != "" {
221+ t.CronSpec = r.Cron
222+ }
223+ if r.AllBranches != nil {
224+ t.AllBranches = *r.AllBranches
225+ }
226+ }
227+ if e := ValidateMirrorURL(t.URL); e != nil {
228+ return t, e
229+ }
230+ c, e := mirrorCron(t.CronSpec)
231+ if e != nil {
232+ return t, e
233+ }
234+ t.Cron = c
235+ t.Enabled = true
236+ return t, nil
237+}
@@ -0,0 +1,237 @@
1+// SPDX-License-Identifier: AGPL-3.0-or-later
2+
3+package domain
4+
5+import (
6+ "fmt"
7+ "net"
8+ "net/url"
9+ "strconv"
10+ "strings"
11+ "time"
12+ "unicode"
13+)
14+
15+const (
16+ MirrorMinInterval = 5 * time.Minute // densest cron allowed
17+ MirrorManualInterval = 60 * time.Second // min gap between manual syncs (not --force)
18+ MirrorRepoPlaceholder = "{repo}"
19+)
20+
21+// MirrorGlobalConfig is the server-wide mirror policy.
22+type MirrorGlobalConfig struct {
23+ Enabled bool
24+ URLTemplate string // ssh URL containing MirrorRepoPlaceholder; "" allowed while disabled
25+ Cron string // 5-field cron, UTC; "" allowed while disabled
26+ AllBranches bool
27+ UpdatedAt time.Time
28+}
29+
30+// MirrorRepoConfig is the per-repository entry: an override (URL != "") or an exclusion.
31+type MirrorRepoConfig struct {
32+ Repo RepoName
33+ URL string // "" means no override
34+ Cron string // "" means inherit global cron
35+ AllBranches *bool // nil means inherit
36+ Excluded bool // opt out of the global template; ignored when URL != ""
37+ UpdatedAt time.Time
38+}
39+type MirrorSource string
40+
41+const (
42+ MirrorSourceNone MirrorSource = "disabled" // nothing applies
43+ MirrorSourceGlobal MirrorSource = "global"
44+ MirrorSourceOverride MirrorSource = "override"
45+ MirrorSourceExcluded MirrorSource = "excluded"
46+)
47+
48+// MirrorTarget is what one repository is mirrored to after resolution.
49+type MirrorTarget struct {
50+ Repo RepoName
51+ Enabled bool
52+ URL string
53+ Cron Cron // zero when !Enabled
54+ CronSpec string
55+ AllBranches bool
56+ Source MirrorSource
57+}
58+
59+func mirrorURLParts(s string) (host, path string, err error) {
60+ bad := func() (string, string, error) {
61+ return "", "", fmt.Errorf("%w: mirror url must be an SSH host and repository path without credentials", ErrInvalid)
62+ }
63+ if strings.ContainsFunc(s, func(r rune) bool { return unicode.IsControl(r) || unicode.IsSpace(r) }) {
64+ return bad()
65+ }
66+ if strings.HasPrefix(s, "ssh://") {
67+ u, e := url.Parse(s)
68+ if e != nil || u.Hostname() == "" || u.RawQuery != "" || u.Fragment != "" {
69+ return bad()
70+ }
71+ if u.User != nil {
72+ if _, ok := u.User.Password(); ok {
73+ return bad()
74+ }
75+ if u.User.Username() == "" {
76+ return bad()
77+ }
78+ }
79+ if p := u.Port(); p != "" {
80+ n, e := strconv.Atoi(p)
81+ if e != nil || n < 1 || n > 65535 {
82+ return bad()
83+ }
84+ }
85+ host = u.Host
86+ path = strings.TrimPrefix(u.Path, "/")
87+ } else {
88+ if strings.Contains(s, "://") {
89+ return bad()
90+ }
91+ var ok bool
92+ host, path, ok = strings.Cut(s, ":")
93+ if !ok {
94+ return bad()
95+ }
96+ if strings.Contains(host, "@") {
97+ user, h, _ := strings.Cut(host, "@")
98+ if user == "" || strings.ContainsAny(user, ":/@") {
99+ return bad()
100+ }
101+ host = h
102+ }
103+ if strings.ContainsAny(host, "/@:") {
104+ return bad()
105+ }
106+ }
107+ if host == "" || strings.HasPrefix(host, "-") || path == "" || strings.HasPrefix(path, "-") || strings.Contains(path, "@") {
108+ return bad()
109+ }
110+ // Reject malformed bracketed addresses as well as empty hosts.
111+ if strings.HasPrefix(host, "[") {
112+ h := strings.TrimSuffix(strings.TrimPrefix(host, "["), "]")
113+ if x, _, e := net.SplitHostPort(host); e == nil {
114+ h = strings.Trim(x, "[]")
115+ }
116+ if net.ParseIP(h) == nil {
117+ return bad()
118+ }
119+ }
120+ return host, path, nil
121+}
122+
123+// ValidateMirrorURL accepts "git@host:path" (scp-like, user optional, no ':' port) and
124+// "ssh://[user@]host[:port]/path". Anything else, an empty host, control chars, or a path
125+// starting with "-" is "%w: mirror url ..." with ErrInvalid. URL is never logged in full
126+// elsewhere: use RedactMirrorURL.
127+func ValidateMirrorURL(s string) error { _, _, err := mirrorURLParts(s); return err }
128+func RedactMirrorURL(s string) string {
129+ h, p, e := mirrorURLParts(s)
130+ if e != nil {
131+ return "[invalid mirror URL]"
132+ }
133+ return h + ":" + p
134+}
135+
136+// ExpandMirrorURL replaces MirrorRepoPlaceholder with repo (as a path segment, not escaped
137+// beyond RepoName's own charset). Returns ErrInvalid when the template has no placeholder.
138+func ExpandMirrorURL(template string, repo RepoName) (string, error) {
139+ if !strings.Contains(template, MirrorRepoPlaceholder) {
140+ return "", fmt.Errorf("%w: mirror url template needs {repo}", ErrInvalid)
141+ }
142+ return strings.ReplaceAll(template, MirrorRepoPlaceholder, repo.String()), nil
143+}
144+func mirrorCron(spec string) (Cron, error) {
145+ c, e := ParseCron(spec)
146+ if e != nil {
147+ return c, e
148+ }
149+ if gap := CronMinGap(c); gap > 0 && gap < MirrorMinInterval {
150+ return Cron{}, fmt.Errorf("%w: cron interval must be at least five minutes", ErrInvalid)
151+ }
152+ return c, nil
153+}
154+
155+// ValidateMirrorGlobalConfig / ValidateMirrorRepoConfig return InvalidFieldsError with fields
156+// "url_template", "cron", "url": cron parsed with ParseCron and checked by CronMinGap >= MirrorMinInterval;
157+// url_template must contain the placeholder; when Enabled both are required.
158+func ValidateMirrorGlobalConfig(g MirrorGlobalConfig) error {
159+ var fields []FieldViolation
160+ if g.Enabled || g.URLTemplate != "" {
161+ repo, _ := ParseRepoName("repo")
162+ u, e := ExpandMirrorURL(g.URLTemplate, repo)
163+ if e == nil {
164+ e = ValidateMirrorURL(u)
165+ }
166+ if e != nil {
167+ fields = append(fields, FieldViolation{"url_template", "must be an SSH URL template containing {repo}"})
168+ }
169+ }
170+ if g.Enabled || g.Cron != "" {
171+ if _, e := mirrorCron(g.Cron); e != nil {
172+ fields = append(fields, FieldViolation{"cron", e.Error()})
173+ }
174+ }
175+ if len(fields) > 0 {
176+ return &InvalidFieldsError{Fields: fields}
177+ }
178+ return nil
179+}
180+func ValidateMirrorRepoConfig(r MirrorRepoConfig) error {
181+ var fields []FieldViolation
182+ if r.URL != "" {
183+ if e := ValidateMirrorURL(r.URL); e != nil {
184+ fields = append(fields, FieldViolation{"url", e.Error()})
185+ }
186+ }
187+ if r.Cron != "" {
188+ if _, e := mirrorCron(r.Cron); e != nil {
189+ fields = append(fields, FieldViolation{"cron", e.Error()})
190+ }
191+ }
192+ if len(fields) > 0 {
193+ return &InvalidFieldsError{Fields: fields}
194+ }
195+ return nil
196+}
197+
198+// ResolveMirror: override wins even when global is disabled; else global, unless excluded.
199+func ResolveMirror(g MirrorGlobalConfig, r *MirrorRepoConfig, repo RepoName) (MirrorTarget, error) {
200+ t := MirrorTarget{Repo: repo, Source: MirrorSourceNone}
201+ if r != nil && r.URL != "" {
202+ t.URL = r.URL
203+ t.Source = MirrorSourceOverride
204+ } else if r != nil && r.Excluded {
205+ t.Source = MirrorSourceExcluded
206+ return t, nil
207+ } else if g.Enabled {
208+ var e error
209+ t.URL, e = ExpandMirrorURL(g.URLTemplate, repo)
210+ if e != nil {
211+ return t, e
212+ }
213+ t.Source = MirrorSourceGlobal
214+ } else {
215+ return t, nil
216+ }
217+ t.CronSpec = g.Cron
218+ t.AllBranches = g.AllBranches
219+ if r != nil {
220+ if r.Cron != "" {
221+ t.CronSpec = r.Cron
222+ }
223+ if r.AllBranches != nil {
224+ t.AllBranches = *r.AllBranches
225+ }
226+ }
227+ if e := ValidateMirrorURL(t.URL); e != nil {
228+ return t, e
229+ }
230+ c, e := mirrorCron(t.CronSpec)
231+ if e != nil {
232+ return t, e
233+ }
234+ t.Cron = c
235+ t.Enabled = true
236+ return t, nil
237+}
A internal/domain/mirror_cron.go +32 -0
@@ -0,0 +1,32 @@
1+// SPDX-License-Identifier: AGPL-3.0-or-later
2+package domain
3+
4+import "time"
5+
6+func CronMinGap(c Cron) time.Duration {
7+ start := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
8+ end := start.AddDate(1, 0, 0)
9+ var prev time.Time
10+ var gap time.Duration
11+ for t := start; t.Before(end); t = t.Add(time.Minute) {
12+ if c.Matches(t) {
13+ if !prev.IsZero() {
14+ d := t.Sub(prev)
15+ if gap == 0 || d < gap {
16+ gap = d
17+ }
18+ }
19+ prev = t
20+ }
21+ }
22+ return gap
23+}
24+func NextCron(s CronSchedule, t time.Time) (time.Time, bool) {
25+ end := t.Add(400 * 24 * time.Hour)
26+ for n := t.Truncate(time.Minute).Add(time.Minute); !n.After(end); n = n.Add(time.Minute) {
27+ if s.Due(n) {
28+ return n, true
29+ }
30+ }
31+ return time.Time{}, false
32+}
@@ -0,0 +1,32 @@
1+// SPDX-License-Identifier: AGPL-3.0-or-later
2+package domain
3+
4+import "time"
5+
6+func CronMinGap(c Cron) time.Duration {
7+ start := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
8+ end := start.AddDate(1, 0, 0)
9+ var prev time.Time
10+ var gap time.Duration
11+ for t := start; t.Before(end); t = t.Add(time.Minute) {
12+ if c.Matches(t) {
13+ if !prev.IsZero() {
14+ d := t.Sub(prev)
15+ if gap == 0 || d < gap {
16+ gap = d
17+ }
18+ }
19+ prev = t
20+ }
21+ }
22+ return gap
23+}
24+func NextCron(s CronSchedule, t time.Time) (time.Time, bool) {
25+ end := t.Add(400 * 24 * time.Hour)
26+ for n := t.Truncate(time.Minute).Add(time.Minute); !n.After(end); n = n.Add(time.Minute) {
27+ if s.Due(n) {
28+ return n, true
29+ }
30+ }
31+ return time.Time{}, false
32+}
A internal/domain/mirror_key.go +39 -0
@@ -0,0 +1,39 @@
1+// SPDX-License-Identifier: AGPL-3.0-or-later
2+
3+package domain
4+
5+import (
6+ "crypto/ed25519"
7+ "crypto/sha256"
8+ "encoding/base64"
9+ "encoding/binary"
10+ "net/url"
11+ "time"
12+)
13+
14+const MirrorKeyPath = "mirror/key" // KV key and secretbox AAD of the sealed seed
15+type MirrorPublicKey struct {
16+ AuthorizedKey string // "ssh-ed25519 AAAA… ermite-mirror@<host>"
17+ Fingerprint string // "SHA256:<base64 no padding>"
18+ CreatedAt time.Time
19+}
20+
21+func MirrorKeyComment(publicURL string) string {
22+ host := "localhost"
23+ if u, e := url.Parse(publicURL); e == nil && u.Hostname() != "" {
24+ host = u.Hostname()
25+ }
26+ return "ermite-mirror@" + host
27+}
28+
29+// MirrorPublicKeyOf formats an ed25519 public key (wire encoding + base64 done with stdlib).
30+func MirrorPublicKeyOf(pub ed25519.PublicKey, host string, createdAt time.Time) MirrorPublicKey {
31+ wire := make([]byte, 4+len("ssh-ed25519")+4+len(pub))
32+ binary.BigEndian.PutUint32(wire, uint32(len("ssh-ed25519")))
33+ copy(wire[4:], "ssh-ed25519")
34+ offset := 4 + len("ssh-ed25519")
35+ binary.BigEndian.PutUint32(wire[offset:], uint32(len(pub)))
36+ copy(wire[offset+4:], pub)
37+ sum := sha256.Sum256(wire)
38+ return MirrorPublicKey{AuthorizedKey: "ssh-ed25519 " + base64.StdEncoding.EncodeToString(wire) + " " + MirrorKeyComment("https://"+host), Fingerprint: "SHA256:" + base64.RawStdEncoding.EncodeToString(sum[:]), CreatedAt: createdAt}
39+}
@@ -0,0 +1,39 @@
1+// SPDX-License-Identifier: AGPL-3.0-or-later
2+
3+package domain
4+
5+import (
6+ "crypto/ed25519"
7+ "crypto/sha256"
8+ "encoding/base64"
9+ "encoding/binary"
10+ "net/url"
11+ "time"
12+)
13+
14+const MirrorKeyPath = "mirror/key" // KV key and secretbox AAD of the sealed seed
15+type MirrorPublicKey struct {
16+ AuthorizedKey string // "ssh-ed25519 AAAA… ermite-mirror@<host>"
17+ Fingerprint string // "SHA256:<base64 no padding>"
18+ CreatedAt time.Time
19+}
20+
21+func MirrorKeyComment(publicURL string) string {
22+ host := "localhost"
23+ if u, e := url.Parse(publicURL); e == nil && u.Hostname() != "" {
24+ host = u.Hostname()
25+ }
26+ return "ermite-mirror@" + host
27+}
28+
29+// MirrorPublicKeyOf formats an ed25519 public key (wire encoding + base64 done with stdlib).
30+func MirrorPublicKeyOf(pub ed25519.PublicKey, host string, createdAt time.Time) MirrorPublicKey {
31+ wire := make([]byte, 4+len("ssh-ed25519")+4+len(pub))
32+ binary.BigEndian.PutUint32(wire, uint32(len("ssh-ed25519")))
33+ copy(wire[4:], "ssh-ed25519")
34+ offset := 4 + len("ssh-ed25519")
35+ binary.BigEndian.PutUint32(wire[offset:], uint32(len(pub)))
36+ copy(wire[offset+4:], pub)
37+ sum := sha256.Sum256(wire)
38+ return MirrorPublicKey{AuthorizedKey: "ssh-ed25519 " + base64.StdEncoding.EncodeToString(wire) + " " + MirrorKeyComment("https://"+host), Fingerprint: "SHA256:" + base64.RawStdEncoding.EncodeToString(sum[:]), CreatedAt: createdAt}
39+}
A internal/domain/mirror_refs.go +55 -0
@@ -0,0 +1,55 @@
1+// SPDX-License-Identifier: AGPL-3.0-or-later
2+
3+package domain
4+
5+import (
6+ "sort"
7+ "strings"
8+ "time"
9+)
10+
11+type MirrorRef struct {
12+ Name string
13+ ID ObjectID
14+} // ID = Ref.ID (tag object for annotated tags)
15+// MirrorSnapshot is the last pushed set, persisted; Equal makes the sync a no-op.
16+type MirrorSnapshot struct {
17+ URL string
18+ Refs []MirrorRef
19+ AllBranches bool
20+ At time.Time
21+}
22+
23+// MirrorRefspecs selects refs/heads/<default> (or every refs/heads/* with allBranches) and
24+// refs/tags/*, from the refs of RepoReader.ListRefs (HEAD skipped, unborn default skipped).
25+// Names are plain refs; the caller builds "<ref>:<ref>" (never "+", never a delete).
26+// Returned sorted by ref name.
27+func MirrorRefspecs(refs []Ref, defaultBranch string, allBranches bool) []MirrorRef {
28+ var out []MirrorRef
29+ for _, r := range refs {
30+ if r.ID.IsZero() || r.ID.String() == ZeroOID {
31+ continue
32+ }
33+ if strings.HasPrefix(r.Name, "refs/tags/") || strings.HasPrefix(r.Name, "refs/heads/") && (allBranches || r.Name == "refs/heads/"+defaultBranch) {
34+ out = append(out, MirrorRef{Name: r.Name, ID: r.ID})
35+ }
36+ }
37+ sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name })
38+ return out
39+}
40+func (s MirrorSnapshot) Equal(url string, refs []MirrorRef, allBranches bool) bool {
41+ if s.URL != url || s.AllBranches != allBranches || len(s.Refs) != len(refs) {
42+ return false
43+ }
44+ counts := make(map[MirrorRef]int, len(refs))
45+ for _, r := range s.Refs {
46+ counts[r]++
47+ }
48+ for _, r := range refs {
49+ if counts[r] == 0 {
50+ return false
51+ }
52+ counts[r]--
53+ }
54+ return true
55+}
@@ -0,0 +1,55 @@
1+// SPDX-License-Identifier: AGPL-3.0-or-later
2+
3+package domain
4+
5+import (
6+ "sort"
7+ "strings"
8+ "time"
9+)
10+
11+type MirrorRef struct {
12+ Name string
13+ ID ObjectID
14+} // ID = Ref.ID (tag object for annotated tags)
15+// MirrorSnapshot is the last pushed set, persisted; Equal makes the sync a no-op.
16+type MirrorSnapshot struct {
17+ URL string
18+ Refs []MirrorRef
19+ AllBranches bool
20+ At time.Time
21+}
22+
23+// MirrorRefspecs selects refs/heads/<default> (or every refs/heads/* with allBranches) and
24+// refs/tags/*, from the refs of RepoReader.ListRefs (HEAD skipped, unborn default skipped).
25+// Names are plain refs; the caller builds "<ref>:<ref>" (never "+", never a delete).
26+// Returned sorted by ref name.
27+func MirrorRefspecs(refs []Ref, defaultBranch string, allBranches bool) []MirrorRef {
28+ var out []MirrorRef
29+ for _, r := range refs {
30+ if r.ID.IsZero() || r.ID.String() == ZeroOID {
31+ continue
32+ }
33+ if strings.HasPrefix(r.Name, "refs/tags/") || strings.HasPrefix(r.Name, "refs/heads/") && (allBranches || r.Name == "refs/heads/"+defaultBranch) {
34+ out = append(out, MirrorRef{Name: r.Name, ID: r.ID})
35+ }
36+ }
37+ sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name })
38+ return out
39+}
40+func (s MirrorSnapshot) Equal(url string, refs []MirrorRef, allBranches bool) bool {
41+ if s.URL != url || s.AllBranches != allBranches || len(s.Refs) != len(refs) {
42+ return false
43+ }
44+ counts := make(map[MirrorRef]int, len(refs))
45+ for _, r := range s.Refs {
46+ counts[r]++
47+ }
48+ for _, r := range refs {
49+ if counts[r] == 0 {
50+ return false
51+ }
52+ counts[r]--
53+ }
54+ return true
55+}
A internal/domain/mirror_state.go +190 -0
@@ -0,0 +1,190 @@
1+// SPDX-License-Identifier: AGPL-3.0-or-later
2+
3+package domain
4+
5+import (
6+ "fmt"
7+ "strings"
8+ "time"
9+)
10+
11+type MirrorState string
12+
13+const (
14+ MirrorDisabled MirrorState = "disabled"
15+ MirrorOK MirrorState = "ok"
16+ MirrorFailing MirrorState = "failing"
17+ MirrorPaused MirrorState = "paused"
18+)
19+
20+type MirrorFailureClass string
21+
22+const (
23+ MirrorClassNone MirrorFailureClass = ""
24+ MirrorClassDiverged MirrorFailureClass = "diverged" // non-fast-forward / fetch-first -> paused
25+ MirrorClassAuthDenied MirrorFailureClass = "auth_denied" // "Permission denied (publickey)" -> paused
26+ MirrorClassRemoteNotFound MirrorFailureClass = "remote_not_found" // "Repository not found", "does not appear to be a git repository" -> paused
27+ MirrorClassHostKey MirrorFailureClass = "host_key" // known_hosts mismatch -> paused
28+ MirrorClassTransient MirrorFailureClass = "transient" // timeout, network, exit without a known marker -> backoff
29+ MirrorClassInternal MirrorFailureClass = "internal" // no key, no secrets key, local git error -> backoff
30+)
31+
32+// MirrorError is what the transport returns: Class decides the transition, Detail (capped,
33+// URL-redacted stderr tail) is shown to admins only. Unwrap() yields ErrUnavailable for
34+// transient, ErrPreconditionFailed for the paused classes.
35+type MirrorError struct {
36+ Class MirrorFailureClass
37+ Detail string
38+}
39+
40+// MirrorStatus is the persisted per-repository state.
41+type MirrorStatus struct {
42+ Repo RepoName
43+ State MirrorState
44+ Class MirrorFailureClass
45+ Reason string // MirrorReason(...) output, human
46+ Detail string // last error detail (admin only)
47+ ConsecutiveFailures int
48+ LastAttemptAt time.Time
49+ LastSuccessAt time.Time
50+ NextRetryAt time.Time // zero unless Failing
51+ PausedAt time.Time
52+ UpdatedAt time.Time
53+}
54+type MirrorOutcome struct {
55+ Err *MirrorError
56+ NoOp bool
57+} // Err nil == success
58+type MirrorTransition struct {
59+ Status MirrorStatus
60+ FailedEvent bool // State moved into Failing or Paused from OK/Disabled, or Failing -> Paused
61+ Recovered bool // State moved from Failing/Paused to OK
62+}
63+
64+const (
65+ MirrorBackoffBase = 5 * time.Minute
66+ MirrorBackoffCap = 6 * time.Hour
67+ MirrorPauseAfter = 5 // consecutive transient failures
68+)
69+
70+func (e *MirrorError) Error() string { return fmt.Sprintf("mirror %s: %s", e.Class, e.Detail) }
71+func mirrorFatal(c MirrorFailureClass) bool {
72+ return c == MirrorClassDiverged || c == MirrorClassAuthDenied || c == MirrorClassRemoteNotFound || c == MirrorClassHostKey
73+}
74+func (e *MirrorError) Unwrap() error {
75+ if mirrorFatal(e.Class) {
76+ return ErrPreconditionFailed
77+ }
78+ return ErrUnavailable
79+}
80+
81+// ClassifyMirrorOutput maps git/ssh stderr to a class (table of substrings, tested).
82+func ClassifyMirrorOutput(stderr string, exitErr error, timedOut bool) MirrorFailureClass {
83+ if timedOut {
84+ return MirrorClassTransient
85+ }
86+ s := strings.ToLower(stderr)
87+ for _, entry := range []struct {
88+ class MirrorFailureClass
89+ markers []string
90+ }{
91+ {MirrorClassHostKey, []string{"host key verification failed", "remote host identification has changed", "host key has changed"}},
92+ {MirrorClassAuthDenied, []string{"permission denied (publickey)", "authentication failed"}},
93+ {MirrorClassRemoteNotFound, []string{"repository not found", "does not appear to be a git repository"}},
94+ {MirrorClassDiverged, []string{"non-fast-forward", "fetch first", "fetch-first"}},
95+ } {
96+ for _, m := range entry.markers {
97+ if strings.Contains(s, m) {
98+ return entry.class
99+ }
100+ }
101+ }
102+ if exitErr != nil {
103+ return MirrorClassTransient
104+ }
105+ return MirrorClassNone
106+}
107+func MirrorBackoff(n int) time.Duration {
108+ d := MirrorBackoffBase
109+ for i := 1; i < n && d < MirrorBackoffCap; i++ {
110+ d *= 2
111+ }
112+ return min(d, MirrorBackoffCap)
113+}
114+
115+// MirrorDisable/MirrorReset are the config-change transitions: reset lifts Paused/Failing to OK
116+// with counters cleared (config changed, or sync --force before the attempt); disable -> Disabled.
117+func MirrorReset(p MirrorStatus, now time.Time) MirrorStatus {
118+ p.State = MirrorOK
119+ p.Class = MirrorClassNone
120+ p.Reason = ""
121+ p.Detail = ""
122+ p.ConsecutiveFailures = 0
123+ p.NextRetryAt = time.Time{}
124+ p.PausedAt = time.Time{}
125+ p.UpdatedAt = now
126+ return p
127+}
128+func MirrorDisable(p MirrorStatus, now time.Time) MirrorStatus {
129+ p = MirrorReset(p, now)
130+ p.State = MirrorDisabled
131+ return p
132+}
133+
134+// ApplyMirrorOutcome is the single transition function (pure):
135+//
136+// success -> OK, counters cleared, LastSuccessAt=now (NoOp also sets LastSuccessAt).
137+// fatal class (Diverged/AuthDenied/RemoteNotFound/HostKey) -> Paused at once.
138+// Transient/Internal -> Failing, n++, NextRetryAt=now+MirrorBackoff(n); n>=MirrorPauseAfter -> Paused.
139+// A Paused status receiving an outcome was synced with force: it is judged like any other.
140+func ApplyMirrorOutcome(prev MirrorStatus, out MirrorOutcome, now time.Time) MirrorTransition {
141+ p := MirrorReset(prev, now)
142+ p.LastAttemptAt = now
143+ if out.Err == nil {
144+ p.LastSuccessAt = now
145+ return MirrorTransition{Status: p, Recovered: prev.State == MirrorFailing || prev.State == MirrorPaused}
146+ }
147+ p.Class = out.Err.Class
148+ p.Detail = out.Err.Detail
149+ p.ConsecutiveFailures = prev.ConsecutiveFailures + 1
150+ p.State = MirrorFailing
151+ if mirrorFatal(p.Class) || p.ConsecutiveFailures >= MirrorPauseAfter {
152+ p.State = MirrorPaused
153+ p.PausedAt = now
154+ if prev.State == MirrorPaused && !prev.PausedAt.IsZero() {
155+ p.PausedAt = prev.PausedAt
156+ }
157+ } else {
158+ p.NextRetryAt = now.Add(MirrorBackoff(p.ConsecutiveFailures))
159+ }
160+ p.Reason = MirrorReason(p)
161+ return MirrorTransition{Status: p, FailedEvent: prev.State != MirrorFailing && prev.State != MirrorPaused || prev.State == MirrorFailing && p.State == MirrorPaused}
162+}
163+
164+// MirrorReason is the human sentence of a status, e.g.
165+//
166+// diverged: "the remote has commits this repository does not: push is never forced; fix the remote and run `ermite mirror sync <repo> --force`"
167+// auth_denied: "the remote refused the mirror key: add the output of `ermite mirror key show` to the remote"
168+// remote_not_found: "the remote repository does not exist or is not reachable with this key"
169+// host_key: "the host key of the remote changed: check <data dir>/mirror_known_hosts"
170+// transient: "the last %d attempts failed (%s); next retry %s"
171+// paused after transient: "paused after %d consecutive failures; run `ermite mirror sync <repo> --force`"
172+func MirrorReason(s MirrorStatus) string {
173+ if s.State != MirrorFailing && s.State != MirrorPaused {
174+ return ""
175+ }
176+ switch s.Class {
177+ case MirrorClassDiverged:
178+ return "the remote has commits this repository does not: push is never forced; fix the remote and run `ermite mirror sync <repo> --force`"
179+ case MirrorClassAuthDenied:
180+ return "the remote refused the mirror key: add the output of `ermite mirror key show` to the remote"
181+ case MirrorClassRemoteNotFound:
182+ return "the remote repository does not exist or is not reachable with this key"
183+ case MirrorClassHostKey:
184+ return "the host key of the remote changed: check <data dir>/mirror_known_hosts"
185+ }
186+ if s.State == MirrorPaused {
187+ return fmt.Sprintf("paused after %d consecutive failures; run `ermite mirror sync <repo> --force`", s.ConsecutiveFailures)
188+ }
189+ return fmt.Sprintf("the last %d attempts failed (%s); next retry %s", s.ConsecutiveFailures, s.Class, s.NextRetryAt.UTC().Format(time.RFC3339))
190+}
@@ -0,0 +1,190 @@
1+// SPDX-License-Identifier: AGPL-3.0-or-later
2+
3+package domain
4+
5+import (
6+ "fmt"
7+ "strings"
8+ "time"
9+)
10+
11+type MirrorState string
12+
13+const (
14+ MirrorDisabled MirrorState = "disabled"
15+ MirrorOK MirrorState = "ok"
16+ MirrorFailing MirrorState = "failing"
17+ MirrorPaused MirrorState = "paused"
18+)
19+
20+type MirrorFailureClass string
21+
22+const (
23+ MirrorClassNone MirrorFailureClass = ""
24+ MirrorClassDiverged MirrorFailureClass = "diverged" // non-fast-forward / fetch-first -> paused
25+ MirrorClassAuthDenied MirrorFailureClass = "auth_denied" // "Permission denied (publickey)" -> paused
26+ MirrorClassRemoteNotFound MirrorFailureClass = "remote_not_found" // "Repository not found", "does not appear to be a git repository" -> paused
27+ MirrorClassHostKey MirrorFailureClass = "host_key" // known_hosts mismatch -> paused
28+ MirrorClassTransient MirrorFailureClass = "transient" // timeout, network, exit without a known marker -> backoff
29+ MirrorClassInternal MirrorFailureClass = "internal" // no key, no secrets key, local git error -> backoff
30+)
31+
32+// MirrorError is what the transport returns: Class decides the transition, Detail (capped,
33+// URL-redacted stderr tail) is shown to admins only. Unwrap() yields ErrUnavailable for
34+// transient, ErrPreconditionFailed for the paused classes.
35+type MirrorError struct {
36+ Class MirrorFailureClass
37+ Detail string
38+}
39+
40+// MirrorStatus is the persisted per-repository state.
41+type MirrorStatus struct {
42+ Repo RepoName
43+ State MirrorState
44+ Class MirrorFailureClass
45+ Reason string // MirrorReason(...) output, human
46+ Detail string // last error detail (admin only)
47+ ConsecutiveFailures int
48+ LastAttemptAt time.Time
49+ LastSuccessAt time.Time
50+ NextRetryAt time.Time // zero unless Failing
51+ PausedAt time.Time
52+ UpdatedAt time.Time
53+}
54+type MirrorOutcome struct {
55+ Err *MirrorError
56+ NoOp bool
57+} // Err nil == success
58+type MirrorTransition struct {
59+ Status MirrorStatus
60+ FailedEvent bool // State moved into Failing or Paused from OK/Disabled, or Failing -> Paused
61+ Recovered bool // State moved from Failing/Paused to OK
62+}
63+
64+const (
65+ MirrorBackoffBase = 5 * time.Minute
66+ MirrorBackoffCap = 6 * time.Hour
67+ MirrorPauseAfter = 5 // consecutive transient failures
68+)
69+
70+func (e *MirrorError) Error() string { return fmt.Sprintf("mirror %s: %s", e.Class, e.Detail) }
71+func mirrorFatal(c MirrorFailureClass) bool {
72+ return c == MirrorClassDiverged || c == MirrorClassAuthDenied || c == MirrorClassRemoteNotFound || c == MirrorClassHostKey
73+}
74+func (e *MirrorError) Unwrap() error {
75+ if mirrorFatal(e.Class) {
76+ return ErrPreconditionFailed
77+ }
78+ return ErrUnavailable
79+}
80+
81+// ClassifyMirrorOutput maps git/ssh stderr to a class (table of substrings, tested).
82+func ClassifyMirrorOutput(stderr string, exitErr error, timedOut bool) MirrorFailureClass {
83+ if timedOut {
84+ return MirrorClassTransient
85+ }
86+ s := strings.ToLower(stderr)
87+ for _, entry := range []struct {
88+ class MirrorFailureClass
89+ markers []string
90+ }{
91+ {MirrorClassHostKey, []string{"host key verification failed", "remote host identification has changed", "host key has changed"}},
92+ {MirrorClassAuthDenied, []string{"permission denied (publickey)", "authentication failed"}},
93+ {MirrorClassRemoteNotFound, []string{"repository not found", "does not appear to be a git repository"}},
94+ {MirrorClassDiverged, []string{"non-fast-forward", "fetch first", "fetch-first"}},
95+ } {
96+ for _, m := range entry.markers {
97+ if strings.Contains(s, m) {
98+ return entry.class
99+ }
100+ }
101+ }
102+ if exitErr != nil {
103+ return MirrorClassTransient
104+ }
105+ return MirrorClassNone
106+}
107+func MirrorBackoff(n int) time.Duration {
108+ d := MirrorBackoffBase
109+ for i := 1; i < n && d < MirrorBackoffCap; i++ {
110+ d *= 2
111+ }
112+ return min(d, MirrorBackoffCap)
113+}
114+
115+// MirrorDisable/MirrorReset are the config-change transitions: reset lifts Paused/Failing to OK
116+// with counters cleared (config changed, or sync --force before the attempt); disable -> Disabled.
117+func MirrorReset(p MirrorStatus, now time.Time) MirrorStatus {
118+ p.State = MirrorOK
119+ p.Class = MirrorClassNone
120+ p.Reason = ""
121+ p.Detail = ""
122+ p.ConsecutiveFailures = 0
123+ p.NextRetryAt = time.Time{}
124+ p.PausedAt = time.Time{}
125+ p.UpdatedAt = now
126+ return p
127+}
128+func MirrorDisable(p MirrorStatus, now time.Time) MirrorStatus {
129+ p = MirrorReset(p, now)
130+ p.State = MirrorDisabled
131+ return p
132+}
133+
134+// ApplyMirrorOutcome is the single transition function (pure):
135+//
136+// success -> OK, counters cleared, LastSuccessAt=now (NoOp also sets LastSuccessAt).
137+// fatal class (Diverged/AuthDenied/RemoteNotFound/HostKey) -> Paused at once.
138+// Transient/Internal -> Failing, n++, NextRetryAt=now+MirrorBackoff(n); n>=MirrorPauseAfter -> Paused.
139+// A Paused status receiving an outcome was synced with force: it is judged like any other.
140+func ApplyMirrorOutcome(prev MirrorStatus, out MirrorOutcome, now time.Time) MirrorTransition {
141+ p := MirrorReset(prev, now)
142+ p.LastAttemptAt = now
143+ if out.Err == nil {
144+ p.LastSuccessAt = now
145+ return MirrorTransition{Status: p, Recovered: prev.State == MirrorFailing || prev.State == MirrorPaused}
146+ }
147+ p.Class = out.Err.Class
148+ p.Detail = out.Err.Detail
149+ p.ConsecutiveFailures = prev.ConsecutiveFailures + 1
150+ p.State = MirrorFailing
151+ if mirrorFatal(p.Class) || p.ConsecutiveFailures >= MirrorPauseAfter {
152+ p.State = MirrorPaused
153+ p.PausedAt = now
154+ if prev.State == MirrorPaused && !prev.PausedAt.IsZero() {
155+ p.PausedAt = prev.PausedAt
156+ }
157+ } else {
158+ p.NextRetryAt = now.Add(MirrorBackoff(p.ConsecutiveFailures))
159+ }
160+ p.Reason = MirrorReason(p)
161+ return MirrorTransition{Status: p, FailedEvent: prev.State != MirrorFailing && prev.State != MirrorPaused || prev.State == MirrorFailing && p.State == MirrorPaused}
162+}
163+
164+// MirrorReason is the human sentence of a status, e.g.
165+//
166+// diverged: "the remote has commits this repository does not: push is never forced; fix the remote and run `ermite mirror sync <repo> --force`"
167+// auth_denied: "the remote refused the mirror key: add the output of `ermite mirror key show` to the remote"
168+// remote_not_found: "the remote repository does not exist or is not reachable with this key"
169+// host_key: "the host key of the remote changed: check <data dir>/mirror_known_hosts"
170+// transient: "the last %d attempts failed (%s); next retry %s"
171+// paused after transient: "paused after %d consecutive failures; run `ermite mirror sync <repo> --force`"
172+func MirrorReason(s MirrorStatus) string {
173+ if s.State != MirrorFailing && s.State != MirrorPaused {
174+ return ""
175+ }
176+ switch s.Class {
177+ case MirrorClassDiverged:
178+ return "the remote has commits this repository does not: push is never forced; fix the remote and run `ermite mirror sync <repo> --force`"
179+ case MirrorClassAuthDenied:
180+ return "the remote refused the mirror key: add the output of `ermite mirror key show` to the remote"
181+ case MirrorClassRemoteNotFound:
182+ return "the remote repository does not exist or is not reachable with this key"
183+ case MirrorClassHostKey:
184+ return "the host key of the remote changed: check <data dir>/mirror_known_hosts"
185+ }
186+ if s.State == MirrorPaused {
187+ return fmt.Sprintf("paused after %d consecutive failures; run `ermite mirror sync <repo> --force`", s.ConsecutiveFailures)
188+ }
189+ return fmt.Sprintf("the last %d attempts failed (%s); next retry %s", s.ConsecutiveFailures, s.Class, s.NextRetryAt.UTC().Format(time.RFC3339))
190+}
A internal/domain/mirror_test.go +277 -0
@@ -0,0 +1,277 @@
1+// SPDX-License-Identifier: AGPL-3.0-or-later
2+package domain
3+
4+import (
5+ "crypto/ed25519"
6+ "crypto/sha256"
7+ "encoding/base64"
8+ "encoding/binary"
9+ "errors"
10+ "strings"
11+ "testing"
12+ "time"
13+)
14+
15+func TestMirrorURL(t *testing.T) {
16+ for _, tt := range []struct {
17+ s string
18+ ok bool
19+ }{
20+ {"git@host:acme/repo", true}, {"host:repo", true}, {"ssh://git@host:2222/acme/repo", true}, {"ssh://[::1]/repo", true},
21+ {"https://host/repo", false}, {"file:///repo", false}, {"git@host:-path", false}, {"ssh://host/-path", false}, {":repo", false}, {"ssh:///repo", false}, {"ssh://git:secret@host/repo", false}, {"user:secret@host:repo", false}, {"host:repo\n", false}, {"ssh://host:0/repo", false},
22+ } {
23+ t.Run(tt.s, func(t *testing.T) {
24+ e := ValidateMirrorURL(tt.s)
25+ if (e == nil) != tt.ok {
26+ t.Fatalf("error %v", e)
27+ }
28+ if e != nil && !errors.Is(e, ErrInvalid) {
29+ t.Fatal(e)
30+ }
31+ })
32+ }
33+ if got := RedactMirrorURL("ssh://git@host:2222/acme/repo"); got != "host:2222:acme/repo" {
34+ t.Fatal(got)
35+ }
36+ if strings.Contains(RedactMirrorURL("ssh://git:secret@host/repo"), "secret") {
37+ t.Fatal("credential leak")
38+ }
39+}
40+func TestMirrorResolution(t *testing.T) {
41+ repo, _ := ParseRepoName("demo")
42+ off := false
43+ for _, enabled := range []bool{false, true} {
44+ for _, kind := range []string{"none", "exclude", "override"} {
45+ t.Run(kind+map[bool]string{true: "on", false: "off"}[enabled], func(t *testing.T) {
46+ g := MirrorGlobalConfig{Enabled: enabled, URLTemplate: "git@host:org/{repo}", Cron: "*/5 * * * *", AllBranches: true}
47+ var r *MirrorRepoConfig
48+ want := MirrorSourceNone
49+ if enabled {
50+ want = MirrorSourceGlobal
51+ }
52+ if kind == "exclude" {
53+ r = &MirrorRepoConfig{Excluded: true}
54+ want = MirrorSourceExcluded
55+ }
56+ if kind == "override" {
57+ r = &MirrorRepoConfig{URL: "git@other:demo", Cron: "0 3 * * *", AllBranches: &off, Excluded: true}
58+ want = MirrorSourceOverride
59+ }
60+ got, e := ResolveMirror(g, r, repo)
61+ if e != nil || got.Source != want || got.Enabled != (want == MirrorSourceGlobal || want == MirrorSourceOverride) {
62+ t.Fatalf("%+v %v", got, e)
63+ }
64+ if want == MirrorSourceOverride && (got.AllBranches || got.CronSpec != "0 3 * * *") {
65+ t.Fatal(got)
66+ }
67+ if want == MirrorSourceGlobal && got.URL != "git@host:org/demo" {
68+ t.Fatal(got)
69+ }
70+ })
71+ }
72+ }
73+ if _, e := ExpandMirrorURL("host:path", repo); !errors.Is(e, ErrInvalid) {
74+ t.Fatal(e)
75+ }
76+}
77+func TestMirrorCronValidation(t *testing.T) {
78+ for _, tt := range []struct {
79+ spec string
80+ gap time.Duration
81+ valid bool
82+ }{{"*/5 * * * *", 5 * time.Minute, true}, {"*/4 * * * *", 4 * time.Minute, false}, {"0 3 * * *", 24 * time.Hour, true}, {"0 0 29 2 *", 0, true}, {"bad", 0, false}} {
83+ t.Run(tt.spec, func(t *testing.T) {
84+ c, e := ParseCron(tt.spec)
85+ if e == nil && CronMinGap(c) != tt.gap {
86+ t.Fatal(CronMinGap(c))
87+ }
88+ g := MirrorGlobalConfig{Enabled: true, URLTemplate: "git@host:{repo}", Cron: tt.spec}
89+ if (ValidateMirrorGlobalConfig(g) == nil) != tt.valid {
90+ t.Fatal("global validation")
91+ }
92+ if (ValidateMirrorRepoConfig(MirrorRepoConfig{Cron: tt.spec}) == nil) != tt.valid {
93+ t.Fatal("repo validation")
94+ }
95+ })
96+ }
97+ for _, g := range []MirrorGlobalConfig{{}, {URLTemplate: "https://host/{repo}"}, {Enabled: true}, {URLTemplate: "host:path"}} {
98+ e := ValidateMirrorGlobalConfig(g)
99+ if g == (MirrorGlobalConfig{}) {
100+ if e != nil {
101+ t.Fatal(e)
102+ }
103+ } else {
104+ var fields *InvalidFieldsError
105+ if !errors.As(e, &fields) {
106+ t.Fatalf("%v", e)
107+ }
108+ }
109+ }
110+}
111+func TestMirrorNextCron(t *testing.T) {
112+ loc, _ := time.LoadLocation("America/New_York")
113+ c, _ := ParseCron("30 2 * * *")
114+ start := time.Date(2024, 3, 10, 1, 59, 30, 0, loc)
115+ got, ok := NextCron(CronSchedule{Cron: c, Loc: loc}, start)
116+ want := time.Date(2024, 3, 10, 3, 0, 0, 0, loc)
117+ if !ok || !got.Equal(want) {
118+ t.Fatal(got, ok)
119+ }
120+ c, _ = ParseCron("* * * * *")
121+ got, ok = NextCron(CronSchedule{Cron: c}, start)
122+ if !ok || !got.Equal(start.Truncate(time.Minute).Add(time.Minute)) {
123+ t.Fatal(got)
124+ }
125+ if _, ok := NextCron(CronSchedule{}, start); ok {
126+ t.Fatal("empty cron fired")
127+ }
128+}
129+func TestMirrorTransitions(t *testing.T) {
130+ now := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
131+ for _, class := range []MirrorFailureClass{MirrorClassDiverged, MirrorClassAuthDenied, MirrorClassRemoteNotFound, MirrorClassHostKey, MirrorClassTransient, MirrorClassInternal} {
132+ t.Run(string(class), func(t *testing.T) {
133+ p := MirrorStatus{State: MirrorOK}
134+ fatal := mirrorFatal(class)
135+ for n := 1; n <= 6; n++ {
136+ tr := ApplyMirrorOutcome(p, MirrorOutcome{Err: &MirrorError{Class: class, Detail: "detail"}}, now)
137+ want := MirrorFailing
138+ if fatal || n >= 5 {
139+ want = MirrorPaused
140+ }
141+ if tr.Status.State != want || tr.Status.ConsecutiveFailures != n || tr.FailedEvent != (n == 1 || !fatal && n == 5) {
142+ t.Fatalf("n=%d %+v", n, tr)
143+ }
144+ if want == MirrorFailing && !tr.Status.NextRetryAt.Equal(now.Add(MirrorBackoff(n))) {
145+ t.Fatal(tr)
146+ }
147+ if want == MirrorPaused && (!tr.Status.NextRetryAt.IsZero() || tr.Status.PausedAt.IsZero()) {
148+ t.Fatal(tr)
149+ }
150+ p = tr.Status
151+ }
152+ for _, noop := range []bool{false, true} {
153+ tr := ApplyMirrorOutcome(p, MirrorOutcome{NoOp: noop}, now)
154+ if !tr.Recovered || tr.FailedEvent || tr.Status.State != MirrorOK || tr.Status.ConsecutiveFailures != 0 || !tr.Status.LastSuccessAt.Equal(now) || tr.Status.Detail != "" {
155+ t.Fatal(tr)
156+ }
157+ if ApplyMirrorOutcome(tr.Status, MirrorOutcome{}, now).Recovered {
158+ t.Fatal("repeat recovery")
159+ }
160+ }
161+ for _, state := range []MirrorState{MirrorOK, MirrorDisabled} {
162+ tr := ApplyMirrorOutcome(MirrorStatus{State: state}, MirrorOutcome{Err: &MirrorError{Class: class}}, now)
163+ if !tr.FailedEvent {
164+ t.Fatal(tr)
165+ }
166+ }
167+ if r := MirrorReset(p, now); r.State != MirrorOK || r.Class != "" || !r.PausedAt.IsZero() || r.ConsecutiveFailures != 0 {
168+ t.Fatal(r)
169+ }
170+ if d := MirrorDisable(p, now); d.State != MirrorDisabled || d.Reason != "" {
171+ t.Fatal(d)
172+ }
173+ wantErr := ErrUnavailable
174+ if fatal {
175+ wantErr = ErrPreconditionFailed
176+ }
177+ if !errors.Is(&MirrorError{Class: class}, wantErr) {
178+ t.Fatal("unwrap")
179+ }
180+ })
181+ }
182+ for _, tt := range []struct {
183+ n int
184+ want time.Duration
185+ }{{1, 5 * time.Minute}, {2, 10 * time.Minute}, {3, 20 * time.Minute}, {7, 320 * time.Minute}, {8, 6 * time.Hour}, {1000000, 6 * time.Hour}} {
186+ if got := MirrorBackoff(tt.n); got != tt.want {
187+ t.Fatal(tt, got)
188+ }
189+ }
190+}
191+func TestMirrorClassify(t *testing.T) {
192+ for _, tt := range []struct {
193+ s string
194+ c MirrorFailureClass
195+ }{{"non-fast-forward", MirrorClassDiverged}, {"fetch first", MirrorClassDiverged}, {"Permission denied (publickey)", MirrorClassAuthDenied}, {"Repository not found", MirrorClassRemoteNotFound}, {"does not appear to be a git repository", MirrorClassRemoteNotFound}, {"REMOTE HOST IDENTIFICATION HAS CHANGED", MirrorClassHostKey}, {"Host key verification failed", MirrorClassHostKey}, {"timeout", MirrorClassTransient}} {
196+ if c := ClassifyMirrorOutput(tt.s, errors.New("exit"), false); c != tt.c {
197+ t.Fatal(tt, c)
198+ }
199+ }
200+ if ClassifyMirrorOutput("", nil, false) != MirrorClassNone || ClassifyMirrorOutput("non-fast-forward", nil, true) != MirrorClassTransient {
201+ t.Fatal("classification")
202+ }
203+}
204+func TestMirrorRefs(t *testing.T) {
205+ id, _ := ParseObjectID(strings.Repeat("a", 40))
206+ refs := []Ref{{Name: "HEAD", ID: id}, {Name: "refs/heads/main", ID: id}, {Name: "refs/heads/other", ID: id}, {Name: "refs/tags/v1", ID: id}, {Name: "refs/pull/1", ID: id}, {Name: "refs/heads/unborn"}}
207+ for _, all := range []bool{false, true} {
208+ got := MirrorRefspecs(refs, "main", all)
209+ n := 2
210+ if all {
211+ n = 3
212+ }
213+ if len(got) != n {
214+ t.Fatal(got)
215+ }
216+ for i, r := range got {
217+ if strings.Contains(r.Name+":"+r.Name, "+") || i > 0 && got[i-1].Name >= r.Name {
218+ t.Fatal(got)
219+ }
220+ }
221+ snap := MirrorSnapshot{URL: "host:repo", Refs: got, AllBranches: all}
222+ rev := append([]MirrorRef(nil), got...)
223+ rev[0], rev[len(rev)-1] = rev[len(rev)-1], rev[0]
224+ if !snap.Equal(snap.URL, rev, all) || snap.Equal("other", rev, all) || snap.Equal(snap.URL, rev, !all) || snap.Equal(snap.URL, rev[1:], all) {
225+ t.Fatal("snapshot")
226+ }
227+ rev[0].ID = ObjectID{}
228+ if snap.Equal(snap.URL, rev, all) {
229+ t.Fatal("changed ID")
230+ }
231+ }
232+}
233+func TestMirrorKey(t *testing.T) {
234+ for _, tt := range []struct{ url, want string }{{"https://forge.example:443/path", "ermite-mirror@forge.example"}, {"", "ermite-mirror@localhost"}, {":bad", "ermite-mirror@localhost"}} {
235+ if got := MirrorKeyComment(tt.url); got != tt.want {
236+ t.Fatal(got)
237+ }
238+ }
239+ pub := ed25519.NewKeyFromSeed(make([]byte, ed25519.SeedSize)).Public().(ed25519.PublicKey)
240+ now := time.Now()
241+ key := MirrorPublicKeyOf(pub, "forge.example", now)
242+ parts := strings.Fields(key.AuthorizedKey)
243+ if len(parts) != 3 || parts[0] != "ssh-ed25519" || parts[2] != "ermite-mirror@forge.example" || !key.CreatedAt.Equal(now) {
244+ t.Fatal(key)
245+ }
246+ wire, e := base64.StdEncoding.DecodeString(parts[1])
247+ if e != nil || binary.BigEndian.Uint32(wire[:4]) != 11 || string(wire[4:15]) != "ssh-ed25519" || binary.BigEndian.Uint32(wire[15:19]) != 32 || string(wire[19:]) != string(pub) {
248+ t.Fatal("wire encoding")
249+ }
250+ hash := sha256.Sum256(wire)
251+ if key.Fingerprint != "SHA256:"+base64.RawStdEncoding.EncodeToString(hash[:]) {
252+ t.Fatal(key)
253+ }
254+}
255+
256+func TestMirrorRecoveryFromFailing(t *testing.T) {
257+ now := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
258+ for _, state := range []MirrorState{MirrorDisabled, MirrorOK, MirrorFailing, MirrorPaused} {
259+ for _, noop := range []bool{false, true} {
260+ tr := ApplyMirrorOutcome(MirrorStatus{State: state, ConsecutiveFailures: 2, Class: MirrorClassTransient, Detail: "old"}, MirrorOutcome{NoOp: noop}, now)
261+ if tr.Recovered != (state == MirrorFailing || state == MirrorPaused) || tr.FailedEvent || tr.Status.ConsecutiveFailures != 0 || tr.Status.Class != "" || tr.Status.Reason != "" || !tr.Status.NextRetryAt.IsZero() {
262+ t.Fatal(state, tr)
263+ }
264+ }
265+ }
266+}
267+func TestMirrorNextCronFallBack(t *testing.T) {
268+ loc, _ := time.LoadLocation("America/New_York")
269+ c, _ := ParseCron("30 1 * * *")
270+ // After the first 01:30, the repeated 01:30 must not fire again.
271+ start := time.Date(2024, 11, 3, 5, 30, 0, 0, time.UTC)
272+ got, ok := NextCron(CronSchedule{Cron: c, Loc: loc}, start)
273+ want := time.Date(2024, 11, 4, 1, 30, 0, 0, loc)
274+ if !ok || !got.Equal(want) {
275+ t.Fatal(got, ok)
276+ }
277+}
@@ -0,0 +1,277 @@
1+// SPDX-License-Identifier: AGPL-3.0-or-later
2+package domain
3+
4+import (
5+ "crypto/ed25519"
6+ "crypto/sha256"
7+ "encoding/base64"
8+ "encoding/binary"
9+ "errors"
10+ "strings"
11+ "testing"
12+ "time"
13+)
14+
15+func TestMirrorURL(t *testing.T) {
16+ for _, tt := range []struct {
17+ s string
18+ ok bool
19+ }{
20+ {"git@host:acme/repo", true}, {"host:repo", true}, {"ssh://git@host:2222/acme/repo", true}, {"ssh://[::1]/repo", true},
21+ {"https://host/repo", false}, {"file:///repo", false}, {"git@host:-path", false}, {"ssh://host/-path", false}, {":repo", false}, {"ssh:///repo", false}, {"ssh://git:secret@host/repo", false}, {"user:secret@host:repo", false}, {"host:repo\n", false}, {"ssh://host:0/repo", false},
22+ } {
23+ t.Run(tt.s, func(t *testing.T) {
24+ e := ValidateMirrorURL(tt.s)
25+ if (e == nil) != tt.ok {
26+ t.Fatalf("error %v", e)
27+ }
28+ if e != nil && !errors.Is(e, ErrInvalid) {
29+ t.Fatal(e)
30+ }
31+ })
32+ }
33+ if got := RedactMirrorURL("ssh://git@host:2222/acme/repo"); got != "host:2222:acme/repo" {
34+ t.Fatal(got)
35+ }
36+ if strings.Contains(RedactMirrorURL("ssh://git:secret@host/repo"), "secret") {
37+ t.Fatal("credential leak")
38+ }
39+}
40+func TestMirrorResolution(t *testing.T) {
41+ repo, _ := ParseRepoName("demo")
42+ off := false
43+ for _, enabled := range []bool{false, true} {
44+ for _, kind := range []string{"none", "exclude", "override"} {
45+ t.Run(kind+map[bool]string{true: "on", false: "off"}[enabled], func(t *testing.T) {
46+ g := MirrorGlobalConfig{Enabled: enabled, URLTemplate: "git@host:org/{repo}", Cron: "*/5 * * * *", AllBranches: true}
47+ var r *MirrorRepoConfig
48+ want := MirrorSourceNone
49+ if enabled {
50+ want = MirrorSourceGlobal
51+ }
52+ if kind == "exclude" {
53+ r = &MirrorRepoConfig{Excluded: true}
54+ want = MirrorSourceExcluded
55+ }
56+ if kind == "override" {
57+ r = &MirrorRepoConfig{URL: "git@other:demo", Cron: "0 3 * * *", AllBranches: &off, Excluded: true}
58+ want = MirrorSourceOverride
59+ }
60+ got, e := ResolveMirror(g, r, repo)
61+ if e != nil || got.Source != want || got.Enabled != (want == MirrorSourceGlobal || want == MirrorSourceOverride) {
62+ t.Fatalf("%+v %v", got, e)
63+ }
64+ if want == MirrorSourceOverride && (got.AllBranches || got.CronSpec != "0 3 * * *") {
65+ t.Fatal(got)
66+ }
67+ if want == MirrorSourceGlobal && got.URL != "git@host:org/demo" {
68+ t.Fatal(got)
69+ }
70+ })
71+ }
72+ }
73+ if _, e := ExpandMirrorURL("host:path", repo); !errors.Is(e, ErrInvalid) {
74+ t.Fatal(e)
75+ }
76+}
77+func TestMirrorCronValidation(t *testing.T) {
78+ for _, tt := range []struct {
79+ spec string
80+ gap time.Duration
81+ valid bool
82+ }{{"*/5 * * * *", 5 * time.Minute, true}, {"*/4 * * * *", 4 * time.Minute, false}, {"0 3 * * *", 24 * time.Hour, true}, {"0 0 29 2 *", 0, true}, {"bad", 0, false}} {
83+ t.Run(tt.spec, func(t *testing.T) {
84+ c, e := ParseCron(tt.spec)
85+ if e == nil && CronMinGap(c) != tt.gap {
86+ t.Fatal(CronMinGap(c))
87+ }
88+ g := MirrorGlobalConfig{Enabled: true, URLTemplate: "git@host:{repo}", Cron: tt.spec}
89+ if (ValidateMirrorGlobalConfig(g) == nil) != tt.valid {
90+ t.Fatal("global validation")
91+ }
92+ if (ValidateMirrorRepoConfig(MirrorRepoConfig{Cron: tt.spec}) == nil) != tt.valid {
93+ t.Fatal("repo validation")
94+ }
95+ })
96+ }
97+ for _, g := range []MirrorGlobalConfig{{}, {URLTemplate: "https://host/{repo}"}, {Enabled: true}, {URLTemplate: "host:path"}} {
98+ e := ValidateMirrorGlobalConfig(g)
99+ if g == (MirrorGlobalConfig{}) {
100+ if e != nil {
101+ t.Fatal(e)
102+ }
103+ } else {
104+ var fields *InvalidFieldsError
105+ if !errors.As(e, &fields) {
106+ t.Fatalf("%v", e)
107+ }
108+ }
109+ }
110+}
111+func TestMirrorNextCron(t *testing.T) {
112+ loc, _ := time.LoadLocation("America/New_York")
113+ c, _ := ParseCron("30 2 * * *")
114+ start := time.Date(2024, 3, 10, 1, 59, 30, 0, loc)
115+ got, ok := NextCron(CronSchedule{Cron: c, Loc: loc}, start)
116+ want := time.Date(2024, 3, 10, 3, 0, 0, 0, loc)
117+ if !ok || !got.Equal(want) {
118+ t.Fatal(got, ok)
119+ }
120+ c, _ = ParseCron("* * * * *")
121+ got, ok = NextCron(CronSchedule{Cron: c}, start)
122+ if !ok || !got.Equal(start.Truncate(time.Minute).Add(time.Minute)) {
123+ t.Fatal(got)
124+ }
125+ if _, ok := NextCron(CronSchedule{}, start); ok {
126+ t.Fatal("empty cron fired")
127+ }
128+}
129+func TestMirrorTransitions(t *testing.T) {
130+ now := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
131+ for _, class := range []MirrorFailureClass{MirrorClassDiverged, MirrorClassAuthDenied, MirrorClassRemoteNotFound, MirrorClassHostKey, MirrorClassTransient, MirrorClassInternal} {
132+ t.Run(string(class), func(t *testing.T) {
133+ p := MirrorStatus{State: MirrorOK}
134+ fatal := mirrorFatal(class)
135+ for n := 1; n <= 6; n++ {
136+ tr := ApplyMirrorOutcome(p, MirrorOutcome{Err: &MirrorError{Class: class, Detail: "detail"}}, now)
137+ want := MirrorFailing
138+ if fatal || n >= 5 {
139+ want = MirrorPaused
140+ }
141+ if tr.Status.State != want || tr.Status.ConsecutiveFailures != n || tr.FailedEvent != (n == 1 || !fatal && n == 5) {
142+ t.Fatalf("n=%d %+v", n, tr)
143+ }
144+ if want == MirrorFailing && !tr.Status.NextRetryAt.Equal(now.Add(MirrorBackoff(n))) {
145+ t.Fatal(tr)
146+ }
147+ if want == MirrorPaused && (!tr.Status.NextRetryAt.IsZero() || tr.Status.PausedAt.IsZero()) {
148+ t.Fatal(tr)
149+ }
150+ p = tr.Status
151+ }
152+ for _, noop := range []bool{false, true} {
153+ tr := ApplyMirrorOutcome(p, MirrorOutcome{NoOp: noop}, now)
154+ if !tr.Recovered || tr.FailedEvent || tr.Status.State != MirrorOK || tr.Status.ConsecutiveFailures != 0 || !tr.Status.LastSuccessAt.Equal(now) || tr.Status.Detail != "" {
155+ t.Fatal(tr)
156+ }
157+ if ApplyMirrorOutcome(tr.Status, MirrorOutcome{}, now).Recovered {
158+ t.Fatal("repeat recovery")
159+ }
160+ }
161+ for _, state := range []MirrorState{MirrorOK, MirrorDisabled} {
162+ tr := ApplyMirrorOutcome(MirrorStatus{State: state}, MirrorOutcome{Err: &MirrorError{Class: class}}, now)
163+ if !tr.FailedEvent {
164+ t.Fatal(tr)
165+ }
166+ }
167+ if r := MirrorReset(p, now); r.State != MirrorOK || r.Class != "" || !r.PausedAt.IsZero() || r.ConsecutiveFailures != 0 {
168+ t.Fatal(r)
169+ }
170+ if d := MirrorDisable(p, now); d.State != MirrorDisabled || d.Reason != "" {
171+ t.Fatal(d)
172+ }
173+ wantErr := ErrUnavailable
174+ if fatal {
175+ wantErr = ErrPreconditionFailed
176+ }
177+ if !errors.Is(&MirrorError{Class: class}, wantErr) {
178+ t.Fatal("unwrap")
179+ }
180+ })
181+ }
182+ for _, tt := range []struct {
183+ n int
184+ want time.Duration
185+ }{{1, 5 * time.Minute}, {2, 10 * time.Minute}, {3, 20 * time.Minute}, {7, 320 * time.Minute}, {8, 6 * time.Hour}, {1000000, 6 * time.Hour}} {
186+ if got := MirrorBackoff(tt.n); got != tt.want {
187+ t.Fatal(tt, got)
188+ }
189+ }
190+}
191+func TestMirrorClassify(t *testing.T) {
192+ for _, tt := range []struct {
193+ s string
194+ c MirrorFailureClass
195+ }{{"non-fast-forward", MirrorClassDiverged}, {"fetch first", MirrorClassDiverged}, {"Permission denied (publickey)", MirrorClassAuthDenied}, {"Repository not found", MirrorClassRemoteNotFound}, {"does not appear to be a git repository", MirrorClassRemoteNotFound}, {"REMOTE HOST IDENTIFICATION HAS CHANGED", MirrorClassHostKey}, {"Host key verification failed", MirrorClassHostKey}, {"timeout", MirrorClassTransient}} {
196+ if c := ClassifyMirrorOutput(tt.s, errors.New("exit"), false); c != tt.c {
197+ t.Fatal(tt, c)
198+ }
199+ }
200+ if ClassifyMirrorOutput("", nil, false) != MirrorClassNone || ClassifyMirrorOutput("non-fast-forward", nil, true) != MirrorClassTransient {
201+ t.Fatal("classification")
202+ }
203+}
204+func TestMirrorRefs(t *testing.T) {
205+ id, _ := ParseObjectID(strings.Repeat("a", 40))
206+ refs := []Ref{{Name: "HEAD", ID: id}, {Name: "refs/heads/main", ID: id}, {Name: "refs/heads/other", ID: id}, {Name: "refs/tags/v1", ID: id}, {Name: "refs/pull/1", ID: id}, {Name: "refs/heads/unborn"}}
207+ for _, all := range []bool{false, true} {
208+ got := MirrorRefspecs(refs, "main", all)
209+ n := 2
210+ if all {
211+ n = 3
212+ }
213+ if len(got) != n {
214+ t.Fatal(got)
215+ }
216+ for i, r := range got {
217+ if strings.Contains(r.Name+":"+r.Name, "+") || i > 0 && got[i-1].Name >= r.Name {
218+ t.Fatal(got)
219+ }
220+ }
221+ snap := MirrorSnapshot{URL: "host:repo", Refs: got, AllBranches: all}
222+ rev := append([]MirrorRef(nil), got...)
223+ rev[0], rev[len(rev)-1] = rev[len(rev)-1], rev[0]
224+ if !snap.Equal(snap.URL, rev, all) || snap.Equal("other", rev, all) || snap.Equal(snap.URL, rev, !all) || snap.Equal(snap.URL, rev[1:], all) {
225+ t.Fatal("snapshot")
226+ }
227+ rev[0].ID = ObjectID{}
228+ if snap.Equal(snap.URL, rev, all) {
229+ t.Fatal("changed ID")
230+ }
231+ }
232+}
233+func TestMirrorKey(t *testing.T) {
234+ for _, tt := range []struct{ url, want string }{{"https://forge.example:443/path", "ermite-mirror@forge.example"}, {"", "ermite-mirror@localhost"}, {":bad", "ermite-mirror@localhost"}} {
235+ if got := MirrorKeyComment(tt.url); got != tt.want {
236+ t.Fatal(got)
237+ }
238+ }
239+ pub := ed25519.NewKeyFromSeed(make([]byte, ed25519.SeedSize)).Public().(ed25519.PublicKey)
240+ now := time.Now()
241+ key := MirrorPublicKeyOf(pub, "forge.example", now)
242+ parts := strings.Fields(key.AuthorizedKey)
243+ if len(parts) != 3 || parts[0] != "ssh-ed25519" || parts[2] != "ermite-mirror@forge.example" || !key.CreatedAt.Equal(now) {
244+ t.Fatal(key)
245+ }
246+ wire, e := base64.StdEncoding.DecodeString(parts[1])
247+ if e != nil || binary.BigEndian.Uint32(wire[:4]) != 11 || string(wire[4:15]) != "ssh-ed25519" || binary.BigEndian.Uint32(wire[15:19]) != 32 || string(wire[19:]) != string(pub) {
248+ t.Fatal("wire encoding")
249+ }
250+ hash := sha256.Sum256(wire)
251+ if key.Fingerprint != "SHA256:"+base64.RawStdEncoding.EncodeToString(hash[:]) {
252+ t.Fatal(key)
253+ }
254+}
255+
256+func TestMirrorRecoveryFromFailing(t *testing.T) {
257+ now := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
258+ for _, state := range []MirrorState{MirrorDisabled, MirrorOK, MirrorFailing, MirrorPaused} {
259+ for _, noop := range []bool{false, true} {
260+ tr := ApplyMirrorOutcome(MirrorStatus{State: state, ConsecutiveFailures: 2, Class: MirrorClassTransient, Detail: "old"}, MirrorOutcome{NoOp: noop}, now)
261+ if tr.Recovered != (state == MirrorFailing || state == MirrorPaused) || tr.FailedEvent || tr.Status.ConsecutiveFailures != 0 || tr.Status.Class != "" || tr.Status.Reason != "" || !tr.Status.NextRetryAt.IsZero() {
262+ t.Fatal(state, tr)
263+ }
264+ }
265+ }
266+}
267+func TestMirrorNextCronFallBack(t *testing.T) {
268+ loc, _ := time.LoadLocation("America/New_York")
269+ c, _ := ParseCron("30 1 * * *")
270+ // After the first 01:30, the repeated 01:30 must not fire again.
271+ start := time.Date(2024, 11, 3, 5, 30, 0, 0, time.UTC)
272+ got, ok := NextCron(CronSchedule{Cron: c, Loc: loc}, start)
273+ want := time.Date(2024, 11, 4, 1, 30, 0, 0, loc)
274+ if !ok || !got.Equal(want) {
275+ t.Fatal(got, ok)
276+ }
277+}
A internal/ports/mirror.go +112 -0
@@ -0,0 +1,112 @@
1+// SPDX-License-Identifier: AGPL-3.0-or-later
2+
3+package ports
4+
5+import (
6+ "bnema.dev/ermite/internal/domain"
7+ "context"
8+ "crypto/ed25519"
9+ "time"
10+)
11+
12+// MirrorStore keeps the mirror configuration, state and snapshots (kvstore implements it).
13+type MirrorStore interface {
14+ GetGlobal(ctx context.Context) (domain.MirrorGlobalConfig, error) // zero value, nil error when unset
15+ PutGlobal(ctx context.Context, g domain.MirrorGlobalConfig) error
16+ GetRepoConfig(ctx context.Context, repo domain.RepoName) (domain.MirrorRepoConfig, error) // domain.ErrNotFound when none
17+ PutRepoConfig(ctx context.Context, c domain.MirrorRepoConfig) error // ErrNotFound when the repository is gone (guardRepoKey)
18+ DeleteRepoConfig(ctx context.Context, repo domain.RepoName) error // missing is not an error
19+ ListRepoConfigs(ctx context.Context) ([]domain.MirrorRepoConfig, error) // by repo name
20+ GetStatus(ctx context.Context, repo domain.RepoName) (domain.MirrorStatus, error) // ErrNotFound when none
21+ PutStatus(ctx context.Context, s domain.MirrorStatus) error
22+ ListStatus(ctx context.Context) ([]domain.MirrorStatus, error)
23+ GetSnapshot(ctx context.Context, repo domain.RepoName) (domain.MirrorSnapshot, error) // ErrNotFound when none
24+ PutSnapshot(ctx context.Context, repo domain.RepoName, s domain.MirrorSnapshot) error
25+}
26+
27+// MirrorKeyStore keeps the sealed seed of the mirror key. The use case seals/opens it with
28+// SecretSealer (AAD domain.MirrorKeyPath), as OIDC does.
29+type MirrorKeyStore interface {
30+ // GetMirrorKey returns the sealed seed and its creation time, or ErrNotFound.
31+ GetMirrorKey(ctx context.Context) (sealed []byte, createdAt time.Time, err error)
32+ // PutMirrorKey replaces the key; ifAbsent makes it fail with ErrAlreadyExists when one exists.
33+ PutMirrorKey(ctx context.Context, sealed []byte, createdAt time.Time, ifAbsent bool) error
34+}
35+
36+// MirrorPush is one push request to the transport.
37+type MirrorPush struct {
38+ Repo domain.RepoName
39+ URL string
40+ Refspecs []string // "<ref>:<ref>", no "+", from domain.MirrorRefspecs
41+ PrivateKey ed25519.PrivateKey // written to a 0600 file for the duration of the push
42+}
43+type MirrorRefStatus string
44+
45+const (
46+ MirrorRefOK MirrorRefStatus = "ok" // porcelain ' ' or '*'
47+ MirrorRefUpToDate MirrorRefStatus = "up_to_date" // '='
48+ MirrorRefRejected MirrorRefStatus = "rejected" // '!'
49+)
50+
51+type MirrorRefOutcome struct {
52+ Ref string
53+ Status MirrorRefStatus
54+ Reason string
55+}
56+type MirrorPushResult struct {
57+ Refs []MirrorRefOutcome
58+ Duration time.Duration
59+}
60+
61+// MirrorPusher runs `git push --porcelain` (gitpush implements it). A failed push returns a
62+// *domain.MirrorError (classified); ctx cancellation is returned as is. A result with a rejected
63+// ref and err == nil never happens: a rejection is a MirrorError of class diverged.
64+type MirrorPusher interface {
65+ Push(ctx context.Context, req MirrorPush) (MirrorPushResult, error)
66+}
67+
68+// MirrorTrigger is what Pushes.Record tells the mirrors. *usecase.Mirrors implements it.
69+type MirrorTrigger interface {
70+ OnPush(ctx context.Context, repo domain.RepoName, updates []domain.RefUpdate) // never blocks
71+}
72+
73+// MirrorWarner is what the post-receive hook and the web ask. *usecase.Mirrors implements it.
74+type MirrorWarner interface {
75+ // Warning is non-empty when the mirror of repo is failing or paused ("mirror to github.com:x/y is paused: <reason>").
76+ Warning(ctx context.Context, repo domain.RepoName) string
77+ // Badge is the state and reason for a viewer: reason is "" unless p may administer the repository.
78+ Badge(ctx context.Context, p domain.Principal, repo domain.RepoName) (domain.MirrorState, string)
79+}
80+type MirrorSyncResult struct {
81+ Status domain.MirrorStatus
82+ Queued bool
83+ NoOp bool
84+}
85+
86+// MirrorService is the inbound port of the API. Authorization: key show needs a non-anonymous
87+// principal; key rotate, global config and ListStatus need HasOwnerRights(); per-repo config and
88+// Sync need Allows(ScopeRepoAdmin, repo); GetStatus needs read access to the repository.
89+type MirrorService interface {
90+ ShowKey(ctx context.Context, p domain.Principal) (domain.MirrorPublicKey, error) // generates lazily
91+ RotateKey(ctx context.Context, p domain.Principal) (domain.MirrorPublicKey, error)
92+ GetGlobalConfig(ctx context.Context, p domain.Principal) (domain.MirrorGlobalConfig, error)
93+ SetGlobalConfig(ctx context.Context, p domain.Principal, patch MirrorGlobalPatch) (domain.MirrorGlobalConfig, error)
94+ SetRepoMirror(ctx context.Context, p domain.Principal, c domain.MirrorRepoConfig) (domain.MirrorTarget, error)
95+ RemoveRepoMirror(ctx context.Context, p domain.Principal, repo domain.RepoName) error
96+ SetRepoExcluded(ctx context.Context, p domain.Principal, repo domain.RepoName, excluded bool) (domain.MirrorTarget, error)
97+ GetStatus(ctx context.Context, p domain.Principal, repo domain.RepoName) (MirrorStatusView, error)
98+ ListStatus(ctx context.Context, p domain.Principal) ([]MirrorStatusView, error)
99+ Sync(ctx context.Context, p domain.Principal, repo domain.RepoName, force bool) (MirrorSyncResult, error)
100+ SyncAll(ctx context.Context, p domain.Principal, force bool) ([]MirrorSyncResult, error)
101+}
102+type MirrorGlobalPatch struct {
103+ Enabled *bool
104+ URLTemplate *string
105+ Cron *string
106+ AllBranches *bool
107+}
108+type MirrorStatusView struct {
109+ Target domain.MirrorTarget
110+ Status domain.MirrorStatus
111+ NextRunAt time.Time
112+}
@@ -0,0 +1,112 @@
1+// SPDX-License-Identifier: AGPL-3.0-or-later
2+
3+package ports
4+
5+import (
6+ "bnema.dev/ermite/internal/domain"
7+ "context"
8+ "crypto/ed25519"
9+ "time"
10+)
11+
12+// MirrorStore keeps the mirror configuration, state and snapshots (kvstore implements it).
13+type MirrorStore interface {
14+ GetGlobal(ctx context.Context) (domain.MirrorGlobalConfig, error) // zero value, nil error when unset
15+ PutGlobal(ctx context.Context, g domain.MirrorGlobalConfig) error
16+ GetRepoConfig(ctx context.Context, repo domain.RepoName) (domain.MirrorRepoConfig, error) // domain.ErrNotFound when none
17+ PutRepoConfig(ctx context.Context, c domain.MirrorRepoConfig) error // ErrNotFound when the repository is gone (guardRepoKey)
18+ DeleteRepoConfig(ctx context.Context, repo domain.RepoName) error // missing is not an error
19+ ListRepoConfigs(ctx context.Context) ([]domain.MirrorRepoConfig, error) // by repo name
20+ GetStatus(ctx context.Context, repo domain.RepoName) (domain.MirrorStatus, error) // ErrNotFound when none
21+ PutStatus(ctx context.Context, s domain.MirrorStatus) error
22+ ListStatus(ctx context.Context) ([]domain.MirrorStatus, error)
23+ GetSnapshot(ctx context.Context, repo domain.RepoName) (domain.MirrorSnapshot, error) // ErrNotFound when none
24+ PutSnapshot(ctx context.Context, repo domain.RepoName, s domain.MirrorSnapshot) error
25+}
26+
27+// MirrorKeyStore keeps the sealed seed of the mirror key. The use case seals/opens it with
28+// SecretSealer (AAD domain.MirrorKeyPath), as OIDC does.
29+type MirrorKeyStore interface {
30+ // GetMirrorKey returns the sealed seed and its creation time, or ErrNotFound.
31+ GetMirrorKey(ctx context.Context) (sealed []byte, createdAt time.Time, err error)
32+ // PutMirrorKey replaces the key; ifAbsent makes it fail with ErrAlreadyExists when one exists.
33+ PutMirrorKey(ctx context.Context, sealed []byte, createdAt time.Time, ifAbsent bool) error
34+}
35+
36+// MirrorPush is one push request to the transport.
37+type MirrorPush struct {
38+ Repo domain.RepoName
39+ URL string
40+ Refspecs []string // "<ref>:<ref>", no "+", from domain.MirrorRefspecs
41+ PrivateKey ed25519.PrivateKey // written to a 0600 file for the duration of the push
42+}
43+type MirrorRefStatus string
44+
45+const (
46+ MirrorRefOK MirrorRefStatus = "ok" // porcelain ' ' or '*'
47+ MirrorRefUpToDate MirrorRefStatus = "up_to_date" // '='
48+ MirrorRefRejected MirrorRefStatus = "rejected" // '!'
49+)
50+
51+type MirrorRefOutcome struct {
52+ Ref string
53+ Status MirrorRefStatus
54+ Reason string
55+}
56+type MirrorPushResult struct {
57+ Refs []MirrorRefOutcome
58+ Duration time.Duration
59+}
60+
61+// MirrorPusher runs `git push --porcelain` (gitpush implements it). A failed push returns a
62+// *domain.MirrorError (classified); ctx cancellation is returned as is. A result with a rejected
63+// ref and err == nil never happens: a rejection is a MirrorError of class diverged.
64+type MirrorPusher interface {
65+ Push(ctx context.Context, req MirrorPush) (MirrorPushResult, error)
66+}
67+
68+// MirrorTrigger is what Pushes.Record tells the mirrors. *usecase.Mirrors implements it.
69+type MirrorTrigger interface {
70+ OnPush(ctx context.Context, repo domain.RepoName, updates []domain.RefUpdate) // never blocks
71+}
72+
73+// MirrorWarner is what the post-receive hook and the web ask. *usecase.Mirrors implements it.
74+type MirrorWarner interface {
75+ // Warning is non-empty when the mirror of repo is failing or paused ("mirror to github.com:x/y is paused: <reason>").
76+ Warning(ctx context.Context, repo domain.RepoName) string
77+ // Badge is the state and reason for a viewer: reason is "" unless p may administer the repository.
78+ Badge(ctx context.Context, p domain.Principal, repo domain.RepoName) (domain.MirrorState, string)
79+}
80+type MirrorSyncResult struct {
81+ Status domain.MirrorStatus
82+ Queued bool
83+ NoOp bool
84+}
85+
86+// MirrorService is the inbound port of the API. Authorization: key show needs a non-anonymous
87+// principal; key rotate, global config and ListStatus need HasOwnerRights(); per-repo config and
88+// Sync need Allows(ScopeRepoAdmin, repo); GetStatus needs read access to the repository.
89+type MirrorService interface {
90+ ShowKey(ctx context.Context, p domain.Principal) (domain.MirrorPublicKey, error) // generates lazily
91+ RotateKey(ctx context.Context, p domain.Principal) (domain.MirrorPublicKey, error)
92+ GetGlobalConfig(ctx context.Context, p domain.Principal) (domain.MirrorGlobalConfig, error)
93+ SetGlobalConfig(ctx context.Context, p domain.Principal, patch MirrorGlobalPatch) (domain.MirrorGlobalConfig, error)
94+ SetRepoMirror(ctx context.Context, p domain.Principal, c domain.MirrorRepoConfig) (domain.MirrorTarget, error)
95+ RemoveRepoMirror(ctx context.Context, p domain.Principal, repo domain.RepoName) error
96+ SetRepoExcluded(ctx context.Context, p domain.Principal, repo domain.RepoName, excluded bool) (domain.MirrorTarget, error)
97+ GetStatus(ctx context.Context, p domain.Principal, repo domain.RepoName) (MirrorStatusView, error)
98+ ListStatus(ctx context.Context, p domain.Principal) ([]MirrorStatusView, error)
99+ Sync(ctx context.Context, p domain.Principal, repo domain.RepoName, force bool) (MirrorSyncResult, error)
100+ SyncAll(ctx context.Context, p domain.Principal, force bool) ([]MirrorSyncResult, error)
101+}
102+type MirrorGlobalPatch struct {
103+ Enabled *bool
104+ URLTemplate *string
105+ Cron *string
106+ AllBranches *bool
107+}
108+type MirrorStatusView struct {
109+ Target domain.MirrorTarget
110+ Status domain.MirrorStatus
111+ NextRunAt time.Time
112+}
A internal/ports/mocks/MirrorKeyStore.go +185 -0
@@ -0,0 +1,185 @@
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+// NewMirrorKeyStore creates a new instance of MirrorKeyStore. 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 NewMirrorKeyStore(t interface {
17+ mock.TestingT
18+ Cleanup(func())
19+}) *MirrorKeyStore {
20+ if helper, ok := t.(interface{ Helper() }); ok {
21+ helper.Helper()
22+ }
23+
24+ mock := &MirrorKeyStore{}
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+// MirrorKeyStore is an autogenerated mock type for the MirrorKeyStore type
38+type MirrorKeyStore struct {
39+ mock.Mock
40+}
41+
42+type MirrorKeyStore_Expecter struct {
43+ mock *mock.Mock
44+}
45+
46+func (_m *MirrorKeyStore) EXPECT() *MirrorKeyStore_Expecter {
47+ return &MirrorKeyStore_Expecter{mock: &_m.Mock}
48+}
49+
50+// GetMirrorKey provides a mock function for the type MirrorKeyStore
51+func (_mock *MirrorKeyStore) GetMirrorKey(ctx context.Context) ([]byte, time.Time, error) {
52+ ret := _mock.Called(ctx)
53+
54+ if len(ret) == 0 {
55+ panic("no return value specified for GetMirrorKey")
56+ }
57+
58+ var r0 []byte
59+ var r1 time.Time
60+ var r2 error
61+ if returnFunc, ok := ret.Get(0).(func(context.Context) ([]byte, time.Time, error)); ok {
62+ return returnFunc(ctx)
63+ }
64+ if returnFunc, ok := ret.Get(0).(func(context.Context) []byte); ok {
65+ r0 = returnFunc(ctx)
66+ } else {
67+ if ret.Get(0) != nil {
68+ r0 = ret.Get(0).([]byte)
69+ }
70+ }
71+ if returnFunc, ok := ret.Get(1).(func(context.Context) time.Time); ok {
72+ r1 = returnFunc(ctx)
73+ } else {
74+ r1 = ret.Get(1).(time.Time)
75+ }
76+ if returnFunc, ok := ret.Get(2).(func(context.Context) error); ok {
77+ r2 = returnFunc(ctx)
78+ } else {
79+ r2 = ret.Error(2)
80+ }
81+ return r0, r1, r2
82+}
83+
84+// MirrorKeyStore_GetMirrorKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetMirrorKey'
85+type MirrorKeyStore_GetMirrorKey_Call struct {
86+ *mock.Call
87+}
88+
89+// GetMirrorKey is a helper method to define mock.On call
90+// - ctx context.Context
91+func (_e *MirrorKeyStore_Expecter) GetMirrorKey(ctx any) *MirrorKeyStore_GetMirrorKey_Call {
92+ return &MirrorKeyStore_GetMirrorKey_Call{Call: _e.mock.On("GetMirrorKey", ctx)}
93+}
94+
95+func (_c *MirrorKeyStore_GetMirrorKey_Call) Run(run func(ctx context.Context)) *MirrorKeyStore_GetMirrorKey_Call {
96+ _c.Call.Run(func(args mock.Arguments) {
97+ var arg0 context.Context
98+ if args[0] != nil {
99+ arg0 = args[0].(context.Context)
100+ }
101+ run(
102+ arg0,
103+ )
104+ })
105+ return _c
106+}
107+
108+func (_c *MirrorKeyStore_GetMirrorKey_Call) Return(sealed []byte, createdAt time.Time, err error) *MirrorKeyStore_GetMirrorKey_Call {
109+ _c.Call.Return(sealed, createdAt, err)
110+ return _c
111+}
112+
113+func (_c *MirrorKeyStore_GetMirrorKey_Call) RunAndReturn(run func(ctx context.Context) ([]byte, time.Time, error)) *MirrorKeyStore_GetMirrorKey_Call {
114+ _c.Call.Return(run)
115+ return _c
116+}
117+
118+// PutMirrorKey provides a mock function for the type MirrorKeyStore
119+func (_mock *MirrorKeyStore) PutMirrorKey(ctx context.Context, sealed []byte, createdAt time.Time, ifAbsent bool) error {
120+ ret := _mock.Called(ctx, sealed, createdAt, ifAbsent)
121+
122+ if len(ret) == 0 {
123+ panic("no return value specified for PutMirrorKey")
124+ }
125+
126+ var r0 error
127+ if returnFunc, ok := ret.Get(0).(func(context.Context, []byte, time.Time, bool) error); ok {
128+ r0 = returnFunc(ctx, sealed, createdAt, ifAbsent)
129+ } else {
130+ r0 = ret.Error(0)
131+ }
132+ return r0
133+}
134+
135+// MirrorKeyStore_PutMirrorKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PutMirrorKey'
136+type MirrorKeyStore_PutMirrorKey_Call struct {
137+ *mock.Call
138+}
139+
140+// PutMirrorKey is a helper method to define mock.On call
141+// - ctx context.Context
142+// - sealed []byte
143+// - createdAt time.Time
144+// - ifAbsent bool
145+func (_e *MirrorKeyStore_Expecter) PutMirrorKey(ctx any, sealed any, createdAt any, ifAbsent any) *MirrorKeyStore_PutMirrorKey_Call {
146+ return &MirrorKeyStore_PutMirrorKey_Call{Call: _e.mock.On("PutMirrorKey", ctx, sealed, createdAt, ifAbsent)}
147+}
148+
149+func (_c *MirrorKeyStore_PutMirrorKey_Call) Run(run func(ctx context.Context, sealed []byte, createdAt time.Time, ifAbsent bool)) *MirrorKeyStore_PutMirrorKey_Call {
150+ _c.Call.Run(func(args mock.Arguments) {
151+ var arg0 context.Context
152+ if args[0] != nil {
153+ arg0 = args[0].(context.Context)
154+ }
155+ var arg1 []byte
156+ if args[1] != nil {
157+ arg1 = args[1].([]byte)
158+ }
159+ var arg2 time.Time
160+ if args[2] != nil {
161+ arg2 = args[2].(time.Time)
162+ }
163+ var arg3 bool
164+ if args[3] != nil {
165+ arg3 = args[3].(bool)
166+ }
167+ run(
168+ arg0,
169+ arg1,
170+ arg2,
171+ arg3,
172+ )
173+ })
174+ return _c
175+}
176+
177+func (_c *MirrorKeyStore_PutMirrorKey_Call) Return(err error) *MirrorKeyStore_PutMirrorKey_Call {
178+ _c.Call.Return(err)
179+ return _c
180+}
181+
182+func (_c *MirrorKeyStore_PutMirrorKey_Call) RunAndReturn(run func(ctx context.Context, sealed []byte, createdAt time.Time, ifAbsent bool) error) *MirrorKeyStore_PutMirrorKey_Call {
183+ _c.Call.Return(run)
184+ return _c
185+}
@@ -0,0 +1,185 @@
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+// NewMirrorKeyStore creates a new instance of MirrorKeyStore. 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 NewMirrorKeyStore(t interface {
17+ mock.TestingT
18+ Cleanup(func())
19+}) *MirrorKeyStore {
20+ if helper, ok := t.(interface{ Helper() }); ok {
21+ helper.Helper()
22+ }
23+
24+ mock := &MirrorKeyStore{}
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+// MirrorKeyStore is an autogenerated mock type for the MirrorKeyStore type
38+type MirrorKeyStore struct {
39+ mock.Mock
40+}
41+
42+type MirrorKeyStore_Expecter struct {
43+ mock *mock.Mock
44+}
45+
46+func (_m *MirrorKeyStore) EXPECT() *MirrorKeyStore_Expecter {
47+ return &MirrorKeyStore_Expecter{mock: &_m.Mock}
48+}
49+
50+// GetMirrorKey provides a mock function for the type MirrorKeyStore
51+func (_mock *MirrorKeyStore) GetMirrorKey(ctx context.Context) ([]byte, time.Time, error) {
52+ ret := _mock.Called(ctx)
53+
54+ if len(ret) == 0 {
55+ panic("no return value specified for GetMirrorKey")
56+ }
57+
58+ var r0 []byte
59+ var r1 time.Time
60+ var r2 error
61+ if returnFunc, ok := ret.Get(0).(func(context.Context) ([]byte, time.Time, error)); ok {
62+ return returnFunc(ctx)
63+ }
64+ if returnFunc, ok := ret.Get(0).(func(context.Context) []byte); ok {
65+ r0 = returnFunc(ctx)
66+ } else {
67+ if ret.Get(0) != nil {
68+ r0 = ret.Get(0).([]byte)
69+ }
70+ }
71+ if returnFunc, ok := ret.Get(1).(func(context.Context) time.Time); ok {
72+ r1 = returnFunc(ctx)
73+ } else {
74+ r1 = ret.Get(1).(time.Time)
75+ }
76+ if returnFunc, ok := ret.Get(2).(func(context.Context) error); ok {
77+ r2 = returnFunc(ctx)
78+ } else {
79+ r2 = ret.Error(2)
80+ }
81+ return r0, r1, r2
82+}
83+
84+// MirrorKeyStore_GetMirrorKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetMirrorKey'
85+type MirrorKeyStore_GetMirrorKey_Call struct {
86+ *mock.Call
87+}
88+
89+// GetMirrorKey is a helper method to define mock.On call
90+// - ctx context.Context
91+func (_e *MirrorKeyStore_Expecter) GetMirrorKey(ctx any) *MirrorKeyStore_GetMirrorKey_Call {
92+ return &MirrorKeyStore_GetMirrorKey_Call{Call: _e.mock.On("GetMirrorKey", ctx)}
93+}
94+
95+func (_c *MirrorKeyStore_GetMirrorKey_Call) Run(run func(ctx context.Context)) *MirrorKeyStore_GetMirrorKey_Call {
96+ _c.Call.Run(func(args mock.Arguments) {
97+ var arg0 context.Context
98+ if args[0] != nil {
99+ arg0 = args[0].(context.Context)
100+ }
101+ run(
102+ arg0,
103+ )
104+ })
105+ return _c
106+}
107+
108+func (_c *MirrorKeyStore_GetMirrorKey_Call) Return(sealed []byte, createdAt time.Time, err error) *MirrorKeyStore_GetMirrorKey_Call {
109+ _c.Call.Return(sealed, createdAt, err)
110+ return _c
111+}
112+
113+func (_c *MirrorKeyStore_GetMirrorKey_Call) RunAndReturn(run func(ctx context.Context) ([]byte, time.Time, error)) *MirrorKeyStore_GetMirrorKey_Call {
114+ _c.Call.Return(run)
115+ return _c
116+}
117+
118+// PutMirrorKey provides a mock function for the type MirrorKeyStore
119+func (_mock *MirrorKeyStore) PutMirrorKey(ctx context.Context, sealed []byte, createdAt time.Time, ifAbsent bool) error {
120+ ret := _mock.Called(ctx, sealed, createdAt, ifAbsent)
121+
122+ if len(ret) == 0 {
123+ panic("no return value specified for PutMirrorKey")
124+ }
125+
126+ var r0 error
127+ if returnFunc, ok := ret.Get(0).(func(context.Context, []byte, time.Time, bool) error); ok {
128+ r0 = returnFunc(ctx, sealed, createdAt, ifAbsent)
129+ } else {
130+ r0 = ret.Error(0)
131+ }
132+ return r0
133+}
134+
135+// MirrorKeyStore_PutMirrorKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PutMirrorKey'
136+type MirrorKeyStore_PutMirrorKey_Call struct {
137+ *mock.Call
138+}
139+
140+// PutMirrorKey is a helper method to define mock.On call
141+// - ctx context.Context
142+// - sealed []byte
143+// - createdAt time.Time
144+// - ifAbsent bool
145+func (_e *MirrorKeyStore_Expecter) PutMirrorKey(ctx any, sealed any, createdAt any, ifAbsent any) *MirrorKeyStore_PutMirrorKey_Call {
146+ return &MirrorKeyStore_PutMirrorKey_Call{Call: _e.mock.On("PutMirrorKey", ctx, sealed, createdAt, ifAbsent)}
147+}
148+
149+func (_c *MirrorKeyStore_PutMirrorKey_Call) Run(run func(ctx context.Context, sealed []byte, createdAt time.Time, ifAbsent bool)) *MirrorKeyStore_PutMirrorKey_Call {
150+ _c.Call.Run(func(args mock.Arguments) {
151+ var arg0 context.Context
152+ if args[0] != nil {
153+ arg0 = args[0].(context.Context)
154+ }
155+ var arg1 []byte
156+ if args[1] != nil {
157+ arg1 = args[1].([]byte)
158+ }
159+ var arg2 time.Time
160+ if args[2] != nil {
161+ arg2 = args[2].(time.Time)
162+ }
163+ var arg3 bool
164+ if args[3] != nil {
165+ arg3 = args[3].(bool)
166+ }
167+ run(
168+ arg0,
169+ arg1,
170+ arg2,
171+ arg3,
172+ )
173+ })
174+ return _c
175+}
176+
177+func (_c *MirrorKeyStore_PutMirrorKey_Call) Return(err error) *MirrorKeyStore_PutMirrorKey_Call {
178+ _c.Call.Return(err)
179+ return _c
180+}
181+
182+func (_c *MirrorKeyStore_PutMirrorKey_Call) RunAndReturn(run func(ctx context.Context, sealed []byte, createdAt time.Time, ifAbsent bool) error) *MirrorKeyStore_PutMirrorKey_Call {
183+ _c.Call.Return(run)
184+ return _c
185+}
A internal/ports/mocks/MirrorPusher.go +114 -0
@@ -0,0 +1,114 @@
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/ports"
11+ mock "github.com/stretchr/testify/mock"
12+)
13+
14+// NewMirrorPusher creates a new instance of MirrorPusher. 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 NewMirrorPusher(t interface {
17+ mock.TestingT
18+ Cleanup(func())
19+}) *MirrorPusher {
20+ if helper, ok := t.(interface{ Helper() }); ok {
21+ helper.Helper()
22+ }
23+
24+ mock := &MirrorPusher{}
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+// MirrorPusher is an autogenerated mock type for the MirrorPusher type
38+type MirrorPusher struct {
39+ mock.Mock
40+}
41+
42+type MirrorPusher_Expecter struct {
43+ mock *mock.Mock
44+}
45+
46+func (_m *MirrorPusher) EXPECT() *MirrorPusher_Expecter {
47+ return &MirrorPusher_Expecter{mock: &_m.Mock}
48+}
49+
50+// Push provides a mock function for the type MirrorPusher
51+func (_mock *MirrorPusher) Push(ctx context.Context, req ports.MirrorPush) (ports.MirrorPushResult, error) {
52+ ret := _mock.Called(ctx, req)
53+
54+ if len(ret) == 0 {
55+ panic("no return value specified for Push")
56+ }
57+
58+ var r0 ports.MirrorPushResult
59+ var r1 error
60+ if returnFunc, ok := ret.Get(0).(func(context.Context, ports.MirrorPush) (ports.MirrorPushResult, error)); ok {
61+ return returnFunc(ctx, req)
62+ }
63+ if returnFunc, ok := ret.Get(0).(func(context.Context, ports.MirrorPush) ports.MirrorPushResult); ok {
64+ r0 = returnFunc(ctx, req)
65+ } else {
66+ r0 = ret.Get(0).(ports.MirrorPushResult)
67+ }
68+ if returnFunc, ok := ret.Get(1).(func(context.Context, ports.MirrorPush) error); ok {
69+ r1 = returnFunc(ctx, req)
70+ } else {
71+ r1 = ret.Error(1)
72+ }
73+ return r0, r1
74+}
75+
76+// MirrorPusher_Push_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Push'
77+type MirrorPusher_Push_Call struct {
78+ *mock.Call
79+}
80+
81+// Push is a helper method to define mock.On call
82+// - ctx context.Context
83+// - req ports.MirrorPush
84+func (_e *MirrorPusher_Expecter) Push(ctx any, req any) *MirrorPusher_Push_Call {
85+ return &MirrorPusher_Push_Call{Call: _e.mock.On("Push", ctx, req)}
86+}
87+
88+func (_c *MirrorPusher_Push_Call) Run(run func(ctx context.Context, req ports.MirrorPush)) *MirrorPusher_Push_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 ports.MirrorPush
95+ if args[1] != nil {
96+ arg1 = args[1].(ports.MirrorPush)
97+ }
98+ run(
99+ arg0,
100+ arg1,
101+ )
102+ })
103+ return _c
104+}
105+
106+func (_c *MirrorPusher_Push_Call) Return(mirrorPushResult ports.MirrorPushResult, err error) *MirrorPusher_Push_Call {
107+ _c.Call.Return(mirrorPushResult, err)
108+ return _c
109+}
110+
111+func (_c *MirrorPusher_Push_Call) RunAndReturn(run func(ctx context.Context, req ports.MirrorPush) (ports.MirrorPushResult, error)) *MirrorPusher_Push_Call {
112+ _c.Call.Return(run)
113+ return _c
114+}
@@ -0,0 +1,114 @@
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/ports"
11+ mock "github.com/stretchr/testify/mock"
12+)
13+
14+// NewMirrorPusher creates a new instance of MirrorPusher. 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 NewMirrorPusher(t interface {
17+ mock.TestingT
18+ Cleanup(func())
19+}) *MirrorPusher {
20+ if helper, ok := t.(interface{ Helper() }); ok {
21+ helper.Helper()
22+ }
23+
24+ mock := &MirrorPusher{}
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+// MirrorPusher is an autogenerated mock type for the MirrorPusher type
38+type MirrorPusher struct {
39+ mock.Mock
40+}
41+
42+type MirrorPusher_Expecter struct {
43+ mock *mock.Mock
44+}
45+
46+func (_m *MirrorPusher) EXPECT() *MirrorPusher_Expecter {
47+ return &MirrorPusher_Expecter{mock: &_m.Mock}
48+}
49+
50+// Push provides a mock function for the type MirrorPusher
51+func (_mock *MirrorPusher) Push(ctx context.Context, req ports.MirrorPush) (ports.MirrorPushResult, error) {
52+ ret := _mock.Called(ctx, req)
53+
54+ if len(ret) == 0 {
55+ panic("no return value specified for Push")
56+ }
57+
58+ var r0 ports.MirrorPushResult
59+ var r1 error
60+ if returnFunc, ok := ret.Get(0).(func(context.Context, ports.MirrorPush) (ports.MirrorPushResult, error)); ok {
61+ return returnFunc(ctx, req)
62+ }
63+ if returnFunc, ok := ret.Get(0).(func(context.Context, ports.MirrorPush) ports.MirrorPushResult); ok {
64+ r0 = returnFunc(ctx, req)
65+ } else {
66+ r0 = ret.Get(0).(ports.MirrorPushResult)
67+ }
68+ if returnFunc, ok := ret.Get(1).(func(context.Context, ports.MirrorPush) error); ok {
69+ r1 = returnFunc(ctx, req)
70+ } else {
71+ r1 = ret.Error(1)
72+ }
73+ return r0, r1
74+}
75+
76+// MirrorPusher_Push_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Push'
77+type MirrorPusher_Push_Call struct {
78+ *mock.Call
79+}
80+
81+// Push is a helper method to define mock.On call
82+// - ctx context.Context
83+// - req ports.MirrorPush
84+func (_e *MirrorPusher_Expecter) Push(ctx any, req any) *MirrorPusher_Push_Call {
85+ return &MirrorPusher_Push_Call{Call: _e.mock.On("Push", ctx, req)}
86+}
87+
88+func (_c *MirrorPusher_Push_Call) Run(run func(ctx context.Context, req ports.MirrorPush)) *MirrorPusher_Push_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 ports.MirrorPush
95+ if args[1] != nil {
96+ arg1 = args[1].(ports.MirrorPush)
97+ }
98+ run(
99+ arg0,
100+ arg1,
101+ )
102+ })
103+ return _c
104+}
105+
106+func (_c *MirrorPusher_Push_Call) Return(mirrorPushResult ports.MirrorPushResult, err error) *MirrorPusher_Push_Call {
107+ _c.Call.Return(mirrorPushResult, err)
108+ return _c
109+}
110+
111+func (_c *MirrorPusher_Push_Call) RunAndReturn(run func(ctx context.Context, req ports.MirrorPush) (ports.MirrorPushResult, error)) *MirrorPusher_Push_Call {
112+ _c.Call.Return(run)
113+ return _c
114+}
A internal/ports/mocks/MirrorService.go +824 -0
@@ -0,0 +1,824 @@
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+// NewMirrorService creates a new instance of MirrorService. 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 NewMirrorService(t interface {
18+ mock.TestingT
19+ Cleanup(func())
20+}) *MirrorService {
21+ if helper, ok := t.(interface{ Helper() }); ok {
22+ helper.Helper()
23+ }
24+
25+ mock := &MirrorService{}
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+// MirrorService is an autogenerated mock type for the MirrorService type
39+type MirrorService struct {
40+ mock.Mock
41+}
42+
43+type MirrorService_Expecter struct {
44+ mock *mock.Mock
45+}
46+
47+func (_m *MirrorService) EXPECT() *MirrorService_Expecter {
48+ return &MirrorService_Expecter{mock: &_m.Mock}
49+}
50+
51+// GetGlobalConfig provides a mock function for the type MirrorService
52+func (_mock *MirrorService) GetGlobalConfig(ctx context.Context, p domain.Principal) (domain.MirrorGlobalConfig, error) {
53+ ret := _mock.Called(ctx, p)
54+
55+ if len(ret) == 0 {
56+ panic("no return value specified for GetGlobalConfig")
57+ }
58+
59+ var r0 domain.MirrorGlobalConfig
60+ var r1 error
61+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) (domain.MirrorGlobalConfig, error)); ok {
62+ return returnFunc(ctx, p)
63+ }
64+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) domain.MirrorGlobalConfig); ok {
65+ r0 = returnFunc(ctx, p)
66+ } else {
67+ r0 = ret.Get(0).(domain.MirrorGlobalConfig)
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+// MirrorService_GetGlobalConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetGlobalConfig'
78+type MirrorService_GetGlobalConfig_Call struct {
79+ *mock.Call
80+}
81+
82+// GetGlobalConfig is a helper method to define mock.On call
83+// - ctx context.Context
84+// - p domain.Principal
85+func (_e *MirrorService_Expecter) GetGlobalConfig(ctx any, p any) *MirrorService_GetGlobalConfig_Call {
86+ return &MirrorService_GetGlobalConfig_Call{Call: _e.mock.On("GetGlobalConfig", ctx, p)}
87+}
88+
89+func (_c *MirrorService_GetGlobalConfig_Call) Run(run func(ctx context.Context, p domain.Principal)) *MirrorService_GetGlobalConfig_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 *MirrorService_GetGlobalConfig_Call) Return(mirrorGlobalConfig domain.MirrorGlobalConfig, err error) *MirrorService_GetGlobalConfig_Call {
108+ _c.Call.Return(mirrorGlobalConfig, err)
109+ return _c
110+}
111+
112+func (_c *MirrorService_GetGlobalConfig_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal) (domain.MirrorGlobalConfig, error)) *MirrorService_GetGlobalConfig_Call {
113+ _c.Call.Return(run)
114+ return _c
115+}
116+
117+// GetStatus provides a mock function for the type MirrorService
118+func (_mock *MirrorService) GetStatus(ctx context.Context, p domain.Principal, repo domain.RepoName) (ports.MirrorStatusView, error) {
119+ ret := _mock.Called(ctx, p, repo)
120+
121+ if len(ret) == 0 {
122+ panic("no return value specified for GetStatus")
123+ }
124+
125+ var r0 ports.MirrorStatusView
126+ var r1 error
127+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName) (ports.MirrorStatusView, error)); ok {
128+ return returnFunc(ctx, p, repo)
129+ }
130+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName) ports.MirrorStatusView); ok {
131+ r0 = returnFunc(ctx, p, repo)
132+ } else {
133+ r0 = ret.Get(0).(ports.MirrorStatusView)
134+ }
135+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName) error); ok {
136+ r1 = returnFunc(ctx, p, repo)
137+ } else {
138+ r1 = ret.Error(1)
139+ }
140+ return r0, r1
141+}
142+
143+// MirrorService_GetStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetStatus'
144+type MirrorService_GetStatus_Call struct {
145+ *mock.Call
146+}
147+
148+// GetStatus is a helper method to define mock.On call
149+// - ctx context.Context
150+// - p domain.Principal
151+// - repo domain.RepoName
152+func (_e *MirrorService_Expecter) GetStatus(ctx any, p any, repo any) *MirrorService_GetStatus_Call {
153+ return &MirrorService_GetStatus_Call{Call: _e.mock.On("GetStatus", ctx, p, repo)}
154+}
155+
156+func (_c *MirrorService_GetStatus_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName)) *MirrorService_GetStatus_Call {
157+ _c.Call.Run(func(args mock.Arguments) {
158+ var arg0 context.Context
159+ if args[0] != nil {
160+ arg0 = args[0].(context.Context)
161+ }
162+ var arg1 domain.Principal
163+ if args[1] != nil {
164+ arg1 = args[1].(domain.Principal)
165+ }
166+ var arg2 domain.RepoName
167+ if args[2] != nil {
168+ arg2 = args[2].(domain.RepoName)
169+ }
170+ run(
171+ arg0,
172+ arg1,
173+ arg2,
174+ )
175+ })
176+ return _c
177+}
178+
179+func (_c *MirrorService_GetStatus_Call) Return(mirrorStatusView ports.MirrorStatusView, err error) *MirrorService_GetStatus_Call {
180+ _c.Call.Return(mirrorStatusView, err)
181+ return _c
182+}
183+
184+func (_c *MirrorService_GetStatus_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName) (ports.MirrorStatusView, error)) *MirrorService_GetStatus_Call {
185+ _c.Call.Return(run)
186+ return _c
187+}
188+
189+// ListStatus provides a mock function for the type MirrorService
190+func (_mock *MirrorService) ListStatus(ctx context.Context, p domain.Principal) ([]ports.MirrorStatusView, error) {
191+ ret := _mock.Called(ctx, p)
192+
193+ if len(ret) == 0 {
194+ panic("no return value specified for ListStatus")
195+ }
196+
197+ var r0 []ports.MirrorStatusView
198+ var r1 error
199+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) ([]ports.MirrorStatusView, error)); ok {
200+ return returnFunc(ctx, p)
201+ }
202+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) []ports.MirrorStatusView); ok {
203+ r0 = returnFunc(ctx, p)
204+ } else {
205+ if ret.Get(0) != nil {
206+ r0 = ret.Get(0).([]ports.MirrorStatusView)
207+ }
208+ }
209+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal) error); ok {
210+ r1 = returnFunc(ctx, p)
211+ } else {
212+ r1 = ret.Error(1)
213+ }
214+ return r0, r1
215+}
216+
217+// MirrorService_ListStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListStatus'
218+type MirrorService_ListStatus_Call struct {
219+ *mock.Call
220+}
221+
222+// ListStatus is a helper method to define mock.On call
223+// - ctx context.Context
224+// - p domain.Principal
225+func (_e *MirrorService_Expecter) ListStatus(ctx any, p any) *MirrorService_ListStatus_Call {
226+ return &MirrorService_ListStatus_Call{Call: _e.mock.On("ListStatus", ctx, p)}
227+}
228+
229+func (_c *MirrorService_ListStatus_Call) Run(run func(ctx context.Context, p domain.Principal)) *MirrorService_ListStatus_Call {
230+ _c.Call.Run(func(args mock.Arguments) {
231+ var arg0 context.Context
232+ if args[0] != nil {
233+ arg0 = args[0].(context.Context)
234+ }
235+ var arg1 domain.Principal
236+ if args[1] != nil {
237+ arg1 = args[1].(domain.Principal)
238+ }
239+ run(
240+ arg0,
241+ arg1,
242+ )
243+ })
244+ return _c
245+}
246+
247+func (_c *MirrorService_ListStatus_Call) Return(mirrorStatusViews []ports.MirrorStatusView, err error) *MirrorService_ListStatus_Call {
248+ _c.Call.Return(mirrorStatusViews, err)
249+ return _c
250+}
251+
252+func (_c *MirrorService_ListStatus_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal) ([]ports.MirrorStatusView, error)) *MirrorService_ListStatus_Call {
253+ _c.Call.Return(run)
254+ return _c
255+}
256+
257+// RemoveRepoMirror provides a mock function for the type MirrorService
258+func (_mock *MirrorService) RemoveRepoMirror(ctx context.Context, p domain.Principal, repo domain.RepoName) error {
259+ ret := _mock.Called(ctx, p, repo)
260+
261+ if len(ret) == 0 {
262+ panic("no return value specified for RemoveRepoMirror")
263+ }
264+
265+ var r0 error
266+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName) error); ok {
267+ r0 = returnFunc(ctx, p, repo)
268+ } else {
269+ r0 = ret.Error(0)
270+ }
271+ return r0
272+}
273+
274+// MirrorService_RemoveRepoMirror_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoveRepoMirror'
275+type MirrorService_RemoveRepoMirror_Call struct {
276+ *mock.Call
277+}
278+
279+// RemoveRepoMirror is a helper method to define mock.On call
280+// - ctx context.Context
281+// - p domain.Principal
282+// - repo domain.RepoName
283+func (_e *MirrorService_Expecter) RemoveRepoMirror(ctx any, p any, repo any) *MirrorService_RemoveRepoMirror_Call {
284+ return &MirrorService_RemoveRepoMirror_Call{Call: _e.mock.On("RemoveRepoMirror", ctx, p, repo)}
285+}
286+
287+func (_c *MirrorService_RemoveRepoMirror_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName)) *MirrorService_RemoveRepoMirror_Call {
288+ _c.Call.Run(func(args mock.Arguments) {
289+ var arg0 context.Context
290+ if args[0] != nil {
291+ arg0 = args[0].(context.Context)
292+ }
293+ var arg1 domain.Principal
294+ if args[1] != nil {
295+ arg1 = args[1].(domain.Principal)
296+ }
297+ var arg2 domain.RepoName
298+ if args[2] != nil {
299+ arg2 = args[2].(domain.RepoName)
300+ }
301+ run(
302+ arg0,
303+ arg1,
304+ arg2,
305+ )
306+ })
307+ return _c
308+}
309+
310+func (_c *MirrorService_RemoveRepoMirror_Call) Return(err error) *MirrorService_RemoveRepoMirror_Call {
311+ _c.Call.Return(err)
312+ return _c
313+}
314+
315+func (_c *MirrorService_RemoveRepoMirror_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName) error) *MirrorService_RemoveRepoMirror_Call {
316+ _c.Call.Return(run)
317+ return _c
318+}
319+
320+// RotateKey provides a mock function for the type MirrorService
321+func (_mock *MirrorService) RotateKey(ctx context.Context, p domain.Principal) (domain.MirrorPublicKey, error) {
322+ ret := _mock.Called(ctx, p)
323+
324+ if len(ret) == 0 {
325+ panic("no return value specified for RotateKey")
326+ }
327+
328+ var r0 domain.MirrorPublicKey
329+ var r1 error
330+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) (domain.MirrorPublicKey, error)); ok {
331+ return returnFunc(ctx, p)
332+ }
333+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) domain.MirrorPublicKey); ok {
334+ r0 = returnFunc(ctx, p)
335+ } else {
336+ r0 = ret.Get(0).(domain.MirrorPublicKey)
337+ }
338+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal) error); ok {
339+ r1 = returnFunc(ctx, p)
340+ } else {
341+ r1 = ret.Error(1)
342+ }
343+ return r0, r1
344+}
345+
346+// MirrorService_RotateKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RotateKey'
347+type MirrorService_RotateKey_Call struct {
348+ *mock.Call
349+}
350+
351+// RotateKey is a helper method to define mock.On call
352+// - ctx context.Context
353+// - p domain.Principal
354+func (_e *MirrorService_Expecter) RotateKey(ctx any, p any) *MirrorService_RotateKey_Call {
355+ return &MirrorService_RotateKey_Call{Call: _e.mock.On("RotateKey", ctx, p)}
356+}
357+
358+func (_c *MirrorService_RotateKey_Call) Run(run func(ctx context.Context, p domain.Principal)) *MirrorService_RotateKey_Call {
359+ _c.Call.Run(func(args mock.Arguments) {
360+ var arg0 context.Context
361+ if args[0] != nil {
362+ arg0 = args[0].(context.Context)
363+ }
364+ var arg1 domain.Principal
365+ if args[1] != nil {
366+ arg1 = args[1].(domain.Principal)
367+ }
368+ run(
369+ arg0,
370+ arg1,
371+ )
372+ })
373+ return _c
374+}
375+
376+func (_c *MirrorService_RotateKey_Call) Return(mirrorPublicKey domain.MirrorPublicKey, err error) *MirrorService_RotateKey_Call {
377+ _c.Call.Return(mirrorPublicKey, err)
378+ return _c
379+}
380+
381+func (_c *MirrorService_RotateKey_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal) (domain.MirrorPublicKey, error)) *MirrorService_RotateKey_Call {
382+ _c.Call.Return(run)
383+ return _c
384+}
385+
386+// SetGlobalConfig provides a mock function for the type MirrorService
387+func (_mock *MirrorService) SetGlobalConfig(ctx context.Context, p domain.Principal, patch ports.MirrorGlobalPatch) (domain.MirrorGlobalConfig, error) {
388+ ret := _mock.Called(ctx, p, patch)
389+
390+ if len(ret) == 0 {
391+ panic("no return value specified for SetGlobalConfig")
392+ }
393+
394+ var r0 domain.MirrorGlobalConfig
395+ var r1 error
396+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, ports.MirrorGlobalPatch) (domain.MirrorGlobalConfig, error)); ok {
397+ return returnFunc(ctx, p, patch)
398+ }
399+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, ports.MirrorGlobalPatch) domain.MirrorGlobalConfig); ok {
400+ r0 = returnFunc(ctx, p, patch)
401+ } else {
402+ r0 = ret.Get(0).(domain.MirrorGlobalConfig)
403+ }
404+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, ports.MirrorGlobalPatch) error); ok {
405+ r1 = returnFunc(ctx, p, patch)
406+ } else {
407+ r1 = ret.Error(1)
408+ }
409+ return r0, r1
410+}
411+
412+// MirrorService_SetGlobalConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetGlobalConfig'
413+type MirrorService_SetGlobalConfig_Call struct {
414+ *mock.Call
415+}
416+
417+// SetGlobalConfig is a helper method to define mock.On call
418+// - ctx context.Context
419+// - p domain.Principal
420+// - patch ports.MirrorGlobalPatch
421+func (_e *MirrorService_Expecter) SetGlobalConfig(ctx any, p any, patch any) *MirrorService_SetGlobalConfig_Call {
422+ return &MirrorService_SetGlobalConfig_Call{Call: _e.mock.On("SetGlobalConfig", ctx, p, patch)}
423+}
424+
425+func (_c *MirrorService_SetGlobalConfig_Call) Run(run func(ctx context.Context, p domain.Principal, patch ports.MirrorGlobalPatch)) *MirrorService_SetGlobalConfig_Call {
426+ _c.Call.Run(func(args mock.Arguments) {
427+ var arg0 context.Context
428+ if args[0] != nil {
429+ arg0 = args[0].(context.Context)
430+ }
431+ var arg1 domain.Principal
432+ if args[1] != nil {
433+ arg1 = args[1].(domain.Principal)
434+ }
435+ var arg2 ports.MirrorGlobalPatch
436+ if args[2] != nil {
437+ arg2 = args[2].(ports.MirrorGlobalPatch)
438+ }
439+ run(
440+ arg0,
441+ arg1,
442+ arg2,
443+ )
444+ })
445+ return _c
446+}
447+
448+func (_c *MirrorService_SetGlobalConfig_Call) Return(mirrorGlobalConfig domain.MirrorGlobalConfig, err error) *MirrorService_SetGlobalConfig_Call {
449+ _c.Call.Return(mirrorGlobalConfig, err)
450+ return _c
451+}
452+
453+func (_c *MirrorService_SetGlobalConfig_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, patch ports.MirrorGlobalPatch) (domain.MirrorGlobalConfig, error)) *MirrorService_SetGlobalConfig_Call {
454+ _c.Call.Return(run)
455+ return _c
456+}
457+
458+// SetRepoExcluded provides a mock function for the type MirrorService
459+func (_mock *MirrorService) SetRepoExcluded(ctx context.Context, p domain.Principal, repo domain.RepoName, excluded bool) (domain.MirrorTarget, error) {
460+ ret := _mock.Called(ctx, p, repo, excluded)
461+
462+ if len(ret) == 0 {
463+ panic("no return value specified for SetRepoExcluded")
464+ }
465+
466+ var r0 domain.MirrorTarget
467+ var r1 error
468+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, bool) (domain.MirrorTarget, error)); ok {
469+ return returnFunc(ctx, p, repo, excluded)
470+ }
471+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, bool) domain.MirrorTarget); ok {
472+ r0 = returnFunc(ctx, p, repo, excluded)
473+ } else {
474+ r0 = ret.Get(0).(domain.MirrorTarget)
475+ }
476+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, bool) error); ok {
477+ r1 = returnFunc(ctx, p, repo, excluded)
478+ } else {
479+ r1 = ret.Error(1)
480+ }
481+ return r0, r1
482+}
483+
484+// MirrorService_SetRepoExcluded_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetRepoExcluded'
485+type MirrorService_SetRepoExcluded_Call struct {
486+ *mock.Call
487+}
488+
489+// SetRepoExcluded is a helper method to define mock.On call
490+// - ctx context.Context
491+// - p domain.Principal
492+// - repo domain.RepoName
493+// - excluded bool
494+func (_e *MirrorService_Expecter) SetRepoExcluded(ctx any, p any, repo any, excluded any) *MirrorService_SetRepoExcluded_Call {
495+ return &MirrorService_SetRepoExcluded_Call{Call: _e.mock.On("SetRepoExcluded", ctx, p, repo, excluded)}
496+}
497+
498+func (_c *MirrorService_SetRepoExcluded_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, excluded bool)) *MirrorService_SetRepoExcluded_Call {
499+ _c.Call.Run(func(args mock.Arguments) {
500+ var arg0 context.Context
501+ if args[0] != nil {
502+ arg0 = args[0].(context.Context)
503+ }
504+ var arg1 domain.Principal
505+ if args[1] != nil {
506+ arg1 = args[1].(domain.Principal)
507+ }
508+ var arg2 domain.RepoName
509+ if args[2] != nil {
510+ arg2 = args[2].(domain.RepoName)
511+ }
512+ var arg3 bool
513+ if args[3] != nil {
514+ arg3 = args[3].(bool)
515+ }
516+ run(
517+ arg0,
518+ arg1,
519+ arg2,
520+ arg3,
521+ )
522+ })
523+ return _c
524+}
525+
526+func (_c *MirrorService_SetRepoExcluded_Call) Return(mirrorTarget domain.MirrorTarget, err error) *MirrorService_SetRepoExcluded_Call {
527+ _c.Call.Return(mirrorTarget, err)
528+ return _c
529+}
530+
531+func (_c *MirrorService_SetRepoExcluded_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, excluded bool) (domain.MirrorTarget, error)) *MirrorService_SetRepoExcluded_Call {
532+ _c.Call.Return(run)
533+ return _c
534+}
535+
536+// SetRepoMirror provides a mock function for the type MirrorService
537+func (_mock *MirrorService) SetRepoMirror(ctx context.Context, p domain.Principal, c domain.MirrorRepoConfig) (domain.MirrorTarget, error) {
538+ ret := _mock.Called(ctx, p, c)
539+
540+ if len(ret) == 0 {
541+ panic("no return value specified for SetRepoMirror")
542+ }
543+
544+ var r0 domain.MirrorTarget
545+ var r1 error
546+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.MirrorRepoConfig) (domain.MirrorTarget, error)); ok {
547+ return returnFunc(ctx, p, c)
548+ }
549+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.MirrorRepoConfig) domain.MirrorTarget); ok {
550+ r0 = returnFunc(ctx, p, c)
551+ } else {
552+ r0 = ret.Get(0).(domain.MirrorTarget)
553+ }
554+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.MirrorRepoConfig) error); ok {
555+ r1 = returnFunc(ctx, p, c)
556+ } else {
557+ r1 = ret.Error(1)
558+ }
559+ return r0, r1
560+}
561+
562+// MirrorService_SetRepoMirror_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetRepoMirror'
563+type MirrorService_SetRepoMirror_Call struct {
564+ *mock.Call
565+}
566+
567+// SetRepoMirror is a helper method to define mock.On call
568+// - ctx context.Context
569+// - p domain.Principal
570+// - c domain.MirrorRepoConfig
571+func (_e *MirrorService_Expecter) SetRepoMirror(ctx any, p any, c any) *MirrorService_SetRepoMirror_Call {
572+ return &MirrorService_SetRepoMirror_Call{Call: _e.mock.On("SetRepoMirror", ctx, p, c)}
573+}
574+
575+func (_c *MirrorService_SetRepoMirror_Call) Run(run func(ctx context.Context, p domain.Principal, c domain.MirrorRepoConfig)) *MirrorService_SetRepoMirror_Call {
576+ _c.Call.Run(func(args mock.Arguments) {
577+ var arg0 context.Context
578+ if args[0] != nil {
579+ arg0 = args[0].(context.Context)
580+ }
581+ var arg1 domain.Principal
582+ if args[1] != nil {
583+ arg1 = args[1].(domain.Principal)
584+ }
585+ var arg2 domain.MirrorRepoConfig
586+ if args[2] != nil {
587+ arg2 = args[2].(domain.MirrorRepoConfig)
588+ }
589+ run(
590+ arg0,
591+ arg1,
592+ arg2,
593+ )
594+ })
595+ return _c
596+}
597+
598+func (_c *MirrorService_SetRepoMirror_Call) Return(mirrorTarget domain.MirrorTarget, err error) *MirrorService_SetRepoMirror_Call {
599+ _c.Call.Return(mirrorTarget, err)
600+ return _c
601+}
602+
603+func (_c *MirrorService_SetRepoMirror_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, c domain.MirrorRepoConfig) (domain.MirrorTarget, error)) *MirrorService_SetRepoMirror_Call {
604+ _c.Call.Return(run)
605+ return _c
606+}
607+
608+// ShowKey provides a mock function for the type MirrorService
609+func (_mock *MirrorService) ShowKey(ctx context.Context, p domain.Principal) (domain.MirrorPublicKey, error) {
610+ ret := _mock.Called(ctx, p)
611+
612+ if len(ret) == 0 {
613+ panic("no return value specified for ShowKey")
614+ }
615+
616+ var r0 domain.MirrorPublicKey
617+ var r1 error
618+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) (domain.MirrorPublicKey, error)); ok {
619+ return returnFunc(ctx, p)
620+ }
621+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) domain.MirrorPublicKey); ok {
622+ r0 = returnFunc(ctx, p)
623+ } else {
624+ r0 = ret.Get(0).(domain.MirrorPublicKey)
625+ }
626+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal) error); ok {
627+ r1 = returnFunc(ctx, p)
628+ } else {
629+ r1 = ret.Error(1)
630+ }
631+ return r0, r1
632+}
633+
634+// MirrorService_ShowKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ShowKey'
635+type MirrorService_ShowKey_Call struct {
636+ *mock.Call
637+}
638+
639+// ShowKey is a helper method to define mock.On call
640+// - ctx context.Context
641+// - p domain.Principal
642+func (_e *MirrorService_Expecter) ShowKey(ctx any, p any) *MirrorService_ShowKey_Call {
643+ return &MirrorService_ShowKey_Call{Call: _e.mock.On("ShowKey", ctx, p)}
644+}
645+
646+func (_c *MirrorService_ShowKey_Call) Run(run func(ctx context.Context, p domain.Principal)) *MirrorService_ShowKey_Call {
647+ _c.Call.Run(func(args mock.Arguments) {
648+ var arg0 context.Context
649+ if args[0] != nil {
650+ arg0 = args[0].(context.Context)
651+ }
652+ var arg1 domain.Principal
653+ if args[1] != nil {
654+ arg1 = args[1].(domain.Principal)
655+ }
656+ run(
657+ arg0,
658+ arg1,
659+ )
660+ })
661+ return _c
662+}
663+
664+func (_c *MirrorService_ShowKey_Call) Return(mirrorPublicKey domain.MirrorPublicKey, err error) *MirrorService_ShowKey_Call {
665+ _c.Call.Return(mirrorPublicKey, err)
666+ return _c
667+}
668+
669+func (_c *MirrorService_ShowKey_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal) (domain.MirrorPublicKey, error)) *MirrorService_ShowKey_Call {
670+ _c.Call.Return(run)
671+ return _c
672+}
673+
674+// Sync provides a mock function for the type MirrorService
675+func (_mock *MirrorService) Sync(ctx context.Context, p domain.Principal, repo domain.RepoName, force bool) (ports.MirrorSyncResult, error) {
676+ ret := _mock.Called(ctx, p, repo, force)
677+
678+ if len(ret) == 0 {
679+ panic("no return value specified for Sync")
680+ }
681+
682+ var r0 ports.MirrorSyncResult
683+ var r1 error
684+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, bool) (ports.MirrorSyncResult, error)); ok {
685+ return returnFunc(ctx, p, repo, force)
686+ }
687+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, bool) ports.MirrorSyncResult); ok {
688+ r0 = returnFunc(ctx, p, repo, force)
689+ } else {
690+ r0 = ret.Get(0).(ports.MirrorSyncResult)
691+ }
692+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, bool) error); ok {
693+ r1 = returnFunc(ctx, p, repo, force)
694+ } else {
695+ r1 = ret.Error(1)
696+ }
697+ return r0, r1
698+}
699+
700+// MirrorService_Sync_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Sync'
701+type MirrorService_Sync_Call struct {
702+ *mock.Call
703+}
704+
705+// Sync is a helper method to define mock.On call
706+// - ctx context.Context
707+// - p domain.Principal
708+// - repo domain.RepoName
709+// - force bool
710+func (_e *MirrorService_Expecter) Sync(ctx any, p any, repo any, force any) *MirrorService_Sync_Call {
711+ return &MirrorService_Sync_Call{Call: _e.mock.On("Sync", ctx, p, repo, force)}
712+}
713+
714+func (_c *MirrorService_Sync_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, force bool)) *MirrorService_Sync_Call {
715+ _c.Call.Run(func(args mock.Arguments) {
716+ var arg0 context.Context
717+ if args[0] != nil {
718+ arg0 = args[0].(context.Context)
719+ }
720+ var arg1 domain.Principal
721+ if args[1] != nil {
722+ arg1 = args[1].(domain.Principal)
723+ }
724+ var arg2 domain.RepoName
725+ if args[2] != nil {
726+ arg2 = args[2].(domain.RepoName)
727+ }
728+ var arg3 bool
729+ if args[3] != nil {
730+ arg3 = args[3].(bool)
731+ }
732+ run(
733+ arg0,
734+ arg1,
735+ arg2,
736+ arg3,
737+ )
738+ })
739+ return _c
740+}
741+
742+func (_c *MirrorService_Sync_Call) Return(mirrorSyncResult ports.MirrorSyncResult, err error) *MirrorService_Sync_Call {
743+ _c.Call.Return(mirrorSyncResult, err)
744+ return _c
745+}
746+
747+func (_c *MirrorService_Sync_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, force bool) (ports.MirrorSyncResult, error)) *MirrorService_Sync_Call {
748+ _c.Call.Return(run)
749+ return _c
750+}
751+
752+// SyncAll provides a mock function for the type MirrorService
753+func (_mock *MirrorService) SyncAll(ctx context.Context, p domain.Principal, force bool) ([]ports.MirrorSyncResult, error) {
754+ ret := _mock.Called(ctx, p, force)
755+
756+ if len(ret) == 0 {
757+ panic("no return value specified for SyncAll")
758+ }
759+
760+ var r0 []ports.MirrorSyncResult
761+ var r1 error
762+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, bool) ([]ports.MirrorSyncResult, error)); ok {
763+ return returnFunc(ctx, p, force)
764+ }
765+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, bool) []ports.MirrorSyncResult); ok {
766+ r0 = returnFunc(ctx, p, force)
767+ } else {
768+ if ret.Get(0) != nil {
769+ r0 = ret.Get(0).([]ports.MirrorSyncResult)
770+ }
771+ }
772+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, bool) error); ok {
773+ r1 = returnFunc(ctx, p, force)
774+ } else {
775+ r1 = ret.Error(1)
776+ }
777+ return r0, r1
778+}
779+
780+// MirrorService_SyncAll_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SyncAll'
781+type MirrorService_SyncAll_Call struct {
782+ *mock.Call
783+}
784+
785+// SyncAll is a helper method to define mock.On call
786+// - ctx context.Context
787+// - p domain.Principal
788+// - force bool
789+func (_e *MirrorService_Expecter) SyncAll(ctx any, p any, force any) *MirrorService_SyncAll_Call {
790+ return &MirrorService_SyncAll_Call{Call: _e.mock.On("SyncAll", ctx, p, force)}
791+}
792+
793+func (_c *MirrorService_SyncAll_Call) Run(run func(ctx context.Context, p domain.Principal, force bool)) *MirrorService_SyncAll_Call {
794+ _c.Call.Run(func(args mock.Arguments) {
795+ var arg0 context.Context
796+ if args[0] != nil {
797+ arg0 = args[0].(context.Context)
798+ }
799+ var arg1 domain.Principal
800+ if args[1] != nil {
801+ arg1 = args[1].(domain.Principal)
802+ }
803+ var arg2 bool
804+ if args[2] != nil {
805+ arg2 = args[2].(bool)
806+ }
807+ run(
808+ arg0,
809+ arg1,
810+ arg2,
811+ )
812+ })
813+ return _c
814+}
815+
816+func (_c *MirrorService_SyncAll_Call) Return(mirrorSyncResults []ports.MirrorSyncResult, err error) *MirrorService_SyncAll_Call {
817+ _c.Call.Return(mirrorSyncResults, err)
818+ return _c
819+}
820+
821+func (_c *MirrorService_SyncAll_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, force bool) ([]ports.MirrorSyncResult, error)) *MirrorService_SyncAll_Call {
822+ _c.Call.Return(run)
823+ return _c
824+}
@@ -0,0 +1,824 @@
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+// NewMirrorService creates a new instance of MirrorService. 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 NewMirrorService(t interface {
18+ mock.TestingT
19+ Cleanup(func())
20+}) *MirrorService {
21+ if helper, ok := t.(interface{ Helper() }); ok {
22+ helper.Helper()
23+ }
24+
25+ mock := &MirrorService{}
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+// MirrorService is an autogenerated mock type for the MirrorService type
39+type MirrorService struct {
40+ mock.Mock
41+}
42+
43+type MirrorService_Expecter struct {
44+ mock *mock.Mock
45+}
46+
47+func (_m *MirrorService) EXPECT() *MirrorService_Expecter {
48+ return &MirrorService_Expecter{mock: &_m.Mock}
49+}
50+
51+// GetGlobalConfig provides a mock function for the type MirrorService
52+func (_mock *MirrorService) GetGlobalConfig(ctx context.Context, p domain.Principal) (domain.MirrorGlobalConfig, error) {
53+ ret := _mock.Called(ctx, p)
54+
55+ if len(ret) == 0 {
56+ panic("no return value specified for GetGlobalConfig")
57+ }
58+
59+ var r0 domain.MirrorGlobalConfig
60+ var r1 error
61+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) (domain.MirrorGlobalConfig, error)); ok {
62+ return returnFunc(ctx, p)
63+ }
64+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) domain.MirrorGlobalConfig); ok {
65+ r0 = returnFunc(ctx, p)
66+ } else {
67+ r0 = ret.Get(0).(domain.MirrorGlobalConfig)
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+// MirrorService_GetGlobalConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetGlobalConfig'
78+type MirrorService_GetGlobalConfig_Call struct {
79+ *mock.Call
80+}
81+
82+// GetGlobalConfig is a helper method to define mock.On call
83+// - ctx context.Context
84+// - p domain.Principal
85+func (_e *MirrorService_Expecter) GetGlobalConfig(ctx any, p any) *MirrorService_GetGlobalConfig_Call {
86+ return &MirrorService_GetGlobalConfig_Call{Call: _e.mock.On("GetGlobalConfig", ctx, p)}
87+}
88+
89+func (_c *MirrorService_GetGlobalConfig_Call) Run(run func(ctx context.Context, p domain.Principal)) *MirrorService_GetGlobalConfig_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 *MirrorService_GetGlobalConfig_Call) Return(mirrorGlobalConfig domain.MirrorGlobalConfig, err error) *MirrorService_GetGlobalConfig_Call {
108+ _c.Call.Return(mirrorGlobalConfig, err)
109+ return _c
110+}
111+
112+func (_c *MirrorService_GetGlobalConfig_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal) (domain.MirrorGlobalConfig, error)) *MirrorService_GetGlobalConfig_Call {
113+ _c.Call.Return(run)
114+ return _c
115+}
116+
117+// GetStatus provides a mock function for the type MirrorService
118+func (_mock *MirrorService) GetStatus(ctx context.Context, p domain.Principal, repo domain.RepoName) (ports.MirrorStatusView, error) {
119+ ret := _mock.Called(ctx, p, repo)
120+
121+ if len(ret) == 0 {
122+ panic("no return value specified for GetStatus")
123+ }
124+
125+ var r0 ports.MirrorStatusView
126+ var r1 error
127+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName) (ports.MirrorStatusView, error)); ok {
128+ return returnFunc(ctx, p, repo)
129+ }
130+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName) ports.MirrorStatusView); ok {
131+ r0 = returnFunc(ctx, p, repo)
132+ } else {
133+ r0 = ret.Get(0).(ports.MirrorStatusView)
134+ }
135+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName) error); ok {
136+ r1 = returnFunc(ctx, p, repo)
137+ } else {
138+ r1 = ret.Error(1)
139+ }
140+ return r0, r1
141+}
142+
143+// MirrorService_GetStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetStatus'
144+type MirrorService_GetStatus_Call struct {
145+ *mock.Call
146+}
147+
148+// GetStatus is a helper method to define mock.On call
149+// - ctx context.Context
150+// - p domain.Principal
151+// - repo domain.RepoName
152+func (_e *MirrorService_Expecter) GetStatus(ctx any, p any, repo any) *MirrorService_GetStatus_Call {
153+ return &MirrorService_GetStatus_Call{Call: _e.mock.On("GetStatus", ctx, p, repo)}
154+}
155+
156+func (_c *MirrorService_GetStatus_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName)) *MirrorService_GetStatus_Call {
157+ _c.Call.Run(func(args mock.Arguments) {
158+ var arg0 context.Context
159+ if args[0] != nil {
160+ arg0 = args[0].(context.Context)
161+ }
162+ var arg1 domain.Principal
163+ if args[1] != nil {
164+ arg1 = args[1].(domain.Principal)
165+ }
166+ var arg2 domain.RepoName
167+ if args[2] != nil {
168+ arg2 = args[2].(domain.RepoName)
169+ }
170+ run(
171+ arg0,
172+ arg1,
173+ arg2,
174+ )
175+ })
176+ return _c
177+}
178+
179+func (_c *MirrorService_GetStatus_Call) Return(mirrorStatusView ports.MirrorStatusView, err error) *MirrorService_GetStatus_Call {
180+ _c.Call.Return(mirrorStatusView, err)
181+ return _c
182+}
183+
184+func (_c *MirrorService_GetStatus_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName) (ports.MirrorStatusView, error)) *MirrorService_GetStatus_Call {
185+ _c.Call.Return(run)
186+ return _c
187+}
188+
189+// ListStatus provides a mock function for the type MirrorService
190+func (_mock *MirrorService) ListStatus(ctx context.Context, p domain.Principal) ([]ports.MirrorStatusView, error) {
191+ ret := _mock.Called(ctx, p)
192+
193+ if len(ret) == 0 {
194+ panic("no return value specified for ListStatus")
195+ }
196+
197+ var r0 []ports.MirrorStatusView
198+ var r1 error
199+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) ([]ports.MirrorStatusView, error)); ok {
200+ return returnFunc(ctx, p)
201+ }
202+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) []ports.MirrorStatusView); ok {
203+ r0 = returnFunc(ctx, p)
204+ } else {
205+ if ret.Get(0) != nil {
206+ r0 = ret.Get(0).([]ports.MirrorStatusView)
207+ }
208+ }
209+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal) error); ok {
210+ r1 = returnFunc(ctx, p)
211+ } else {
212+ r1 = ret.Error(1)
213+ }
214+ return r0, r1
215+}
216+
217+// MirrorService_ListStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListStatus'
218+type MirrorService_ListStatus_Call struct {
219+ *mock.Call
220+}
221+
222+// ListStatus is a helper method to define mock.On call
223+// - ctx context.Context
224+// - p domain.Principal
225+func (_e *MirrorService_Expecter) ListStatus(ctx any, p any) *MirrorService_ListStatus_Call {
226+ return &MirrorService_ListStatus_Call{Call: _e.mock.On("ListStatus", ctx, p)}
227+}
228+
229+func (_c *MirrorService_ListStatus_Call) Run(run func(ctx context.Context, p domain.Principal)) *MirrorService_ListStatus_Call {
230+ _c.Call.Run(func(args mock.Arguments) {
231+ var arg0 context.Context
232+ if args[0] != nil {
233+ arg0 = args[0].(context.Context)
234+ }
235+ var arg1 domain.Principal
236+ if args[1] != nil {
237+ arg1 = args[1].(domain.Principal)
238+ }
239+ run(
240+ arg0,
241+ arg1,
242+ )
243+ })
244+ return _c
245+}
246+
247+func (_c *MirrorService_ListStatus_Call) Return(mirrorStatusViews []ports.MirrorStatusView, err error) *MirrorService_ListStatus_Call {
248+ _c.Call.Return(mirrorStatusViews, err)
249+ return _c
250+}
251+
252+func (_c *MirrorService_ListStatus_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal) ([]ports.MirrorStatusView, error)) *MirrorService_ListStatus_Call {
253+ _c.Call.Return(run)
254+ return _c
255+}
256+
257+// RemoveRepoMirror provides a mock function for the type MirrorService
258+func (_mock *MirrorService) RemoveRepoMirror(ctx context.Context, p domain.Principal, repo domain.RepoName) error {
259+ ret := _mock.Called(ctx, p, repo)
260+
261+ if len(ret) == 0 {
262+ panic("no return value specified for RemoveRepoMirror")
263+ }
264+
265+ var r0 error
266+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName) error); ok {
267+ r0 = returnFunc(ctx, p, repo)
268+ } else {
269+ r0 = ret.Error(0)
270+ }
271+ return r0
272+}
273+
274+// MirrorService_RemoveRepoMirror_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoveRepoMirror'
275+type MirrorService_RemoveRepoMirror_Call struct {
276+ *mock.Call
277+}
278+
279+// RemoveRepoMirror is a helper method to define mock.On call
280+// - ctx context.Context
281+// - p domain.Principal
282+// - repo domain.RepoName
283+func (_e *MirrorService_Expecter) RemoveRepoMirror(ctx any, p any, repo any) *MirrorService_RemoveRepoMirror_Call {
284+ return &MirrorService_RemoveRepoMirror_Call{Call: _e.mock.On("RemoveRepoMirror", ctx, p, repo)}
285+}
286+
287+func (_c *MirrorService_RemoveRepoMirror_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName)) *MirrorService_RemoveRepoMirror_Call {
288+ _c.Call.Run(func(args mock.Arguments) {
289+ var arg0 context.Context
290+ if args[0] != nil {
291+ arg0 = args[0].(context.Context)
292+ }
293+ var arg1 domain.Principal
294+ if args[1] != nil {
295+ arg1 = args[1].(domain.Principal)
296+ }
297+ var arg2 domain.RepoName
298+ if args[2] != nil {
299+ arg2 = args[2].(domain.RepoName)
300+ }
301+ run(
302+ arg0,
303+ arg1,
304+ arg2,
305+ )
306+ })
307+ return _c
308+}
309+
310+func (_c *MirrorService_RemoveRepoMirror_Call) Return(err error) *MirrorService_RemoveRepoMirror_Call {
311+ _c.Call.Return(err)
312+ return _c
313+}
314+
315+func (_c *MirrorService_RemoveRepoMirror_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName) error) *MirrorService_RemoveRepoMirror_Call {
316+ _c.Call.Return(run)
317+ return _c
318+}
319+
320+// RotateKey provides a mock function for the type MirrorService
321+func (_mock *MirrorService) RotateKey(ctx context.Context, p domain.Principal) (domain.MirrorPublicKey, error) {
322+ ret := _mock.Called(ctx, p)
323+
324+ if len(ret) == 0 {
325+ panic("no return value specified for RotateKey")
326+ }
327+
328+ var r0 domain.MirrorPublicKey
329+ var r1 error
330+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) (domain.MirrorPublicKey, error)); ok {
331+ return returnFunc(ctx, p)
332+ }
333+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) domain.MirrorPublicKey); ok {
334+ r0 = returnFunc(ctx, p)
335+ } else {
336+ r0 = ret.Get(0).(domain.MirrorPublicKey)
337+ }
338+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal) error); ok {
339+ r1 = returnFunc(ctx, p)
340+ } else {
341+ r1 = ret.Error(1)
342+ }
343+ return r0, r1
344+}
345+
346+// MirrorService_RotateKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RotateKey'
347+type MirrorService_RotateKey_Call struct {
348+ *mock.Call
349+}
350+
351+// RotateKey is a helper method to define mock.On call
352+// - ctx context.Context
353+// - p domain.Principal
354+func (_e *MirrorService_Expecter) RotateKey(ctx any, p any) *MirrorService_RotateKey_Call {
355+ return &MirrorService_RotateKey_Call{Call: _e.mock.On("RotateKey", ctx, p)}
356+}
357+
358+func (_c *MirrorService_RotateKey_Call) Run(run func(ctx context.Context, p domain.Principal)) *MirrorService_RotateKey_Call {
359+ _c.Call.Run(func(args mock.Arguments) {
360+ var arg0 context.Context
361+ if args[0] != nil {
362+ arg0 = args[0].(context.Context)
363+ }
364+ var arg1 domain.Principal
365+ if args[1] != nil {
366+ arg1 = args[1].(domain.Principal)
367+ }
368+ run(
369+ arg0,
370+ arg1,
371+ )
372+ })
373+ return _c
374+}
375+
376+func (_c *MirrorService_RotateKey_Call) Return(mirrorPublicKey domain.MirrorPublicKey, err error) *MirrorService_RotateKey_Call {
377+ _c.Call.Return(mirrorPublicKey, err)
378+ return _c
379+}
380+
381+func (_c *MirrorService_RotateKey_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal) (domain.MirrorPublicKey, error)) *MirrorService_RotateKey_Call {
382+ _c.Call.Return(run)
383+ return _c
384+}
385+
386+// SetGlobalConfig provides a mock function for the type MirrorService
387+func (_mock *MirrorService) SetGlobalConfig(ctx context.Context, p domain.Principal, patch ports.MirrorGlobalPatch) (domain.MirrorGlobalConfig, error) {
388+ ret := _mock.Called(ctx, p, patch)
389+
390+ if len(ret) == 0 {
391+ panic("no return value specified for SetGlobalConfig")
392+ }
393+
394+ var r0 domain.MirrorGlobalConfig
395+ var r1 error
396+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, ports.MirrorGlobalPatch) (domain.MirrorGlobalConfig, error)); ok {
397+ return returnFunc(ctx, p, patch)
398+ }
399+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, ports.MirrorGlobalPatch) domain.MirrorGlobalConfig); ok {
400+ r0 = returnFunc(ctx, p, patch)
401+ } else {
402+ r0 = ret.Get(0).(domain.MirrorGlobalConfig)
403+ }
404+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, ports.MirrorGlobalPatch) error); ok {
405+ r1 = returnFunc(ctx, p, patch)
406+ } else {
407+ r1 = ret.Error(1)
408+ }
409+ return r0, r1
410+}
411+
412+// MirrorService_SetGlobalConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetGlobalConfig'
413+type MirrorService_SetGlobalConfig_Call struct {
414+ *mock.Call
415+}
416+
417+// SetGlobalConfig is a helper method to define mock.On call
418+// - ctx context.Context
419+// - p domain.Principal
420+// - patch ports.MirrorGlobalPatch
421+func (_e *MirrorService_Expecter) SetGlobalConfig(ctx any, p any, patch any) *MirrorService_SetGlobalConfig_Call {
422+ return &MirrorService_SetGlobalConfig_Call{Call: _e.mock.On("SetGlobalConfig", ctx, p, patch)}
423+}
424+
425+func (_c *MirrorService_SetGlobalConfig_Call) Run(run func(ctx context.Context, p domain.Principal, patch ports.MirrorGlobalPatch)) *MirrorService_SetGlobalConfig_Call {
426+ _c.Call.Run(func(args mock.Arguments) {
427+ var arg0 context.Context
428+ if args[0] != nil {
429+ arg0 = args[0].(context.Context)
430+ }
431+ var arg1 domain.Principal
432+ if args[1] != nil {
433+ arg1 = args[1].(domain.Principal)
434+ }
435+ var arg2 ports.MirrorGlobalPatch
436+ if args[2] != nil {
437+ arg2 = args[2].(ports.MirrorGlobalPatch)
438+ }
439+ run(
440+ arg0,
441+ arg1,
442+ arg2,
443+ )
444+ })
445+ return _c
446+}
447+
448+func (_c *MirrorService_SetGlobalConfig_Call) Return(mirrorGlobalConfig domain.MirrorGlobalConfig, err error) *MirrorService_SetGlobalConfig_Call {
449+ _c.Call.Return(mirrorGlobalConfig, err)
450+ return _c
451+}
452+
453+func (_c *MirrorService_SetGlobalConfig_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, patch ports.MirrorGlobalPatch) (domain.MirrorGlobalConfig, error)) *MirrorService_SetGlobalConfig_Call {
454+ _c.Call.Return(run)
455+ return _c
456+}
457+
458+// SetRepoExcluded provides a mock function for the type MirrorService
459+func (_mock *MirrorService) SetRepoExcluded(ctx context.Context, p domain.Principal, repo domain.RepoName, excluded bool) (domain.MirrorTarget, error) {
460+ ret := _mock.Called(ctx, p, repo, excluded)
461+
462+ if len(ret) == 0 {
463+ panic("no return value specified for SetRepoExcluded")
464+ }
465+
466+ var r0 domain.MirrorTarget
467+ var r1 error
468+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, bool) (domain.MirrorTarget, error)); ok {
469+ return returnFunc(ctx, p, repo, excluded)
470+ }
471+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, bool) domain.MirrorTarget); ok {
472+ r0 = returnFunc(ctx, p, repo, excluded)
473+ } else {
474+ r0 = ret.Get(0).(domain.MirrorTarget)
475+ }
476+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, bool) error); ok {
477+ r1 = returnFunc(ctx, p, repo, excluded)
478+ } else {
479+ r1 = ret.Error(1)
480+ }
481+ return r0, r1
482+}
483+
484+// MirrorService_SetRepoExcluded_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetRepoExcluded'
485+type MirrorService_SetRepoExcluded_Call struct {
486+ *mock.Call
487+}
488+
489+// SetRepoExcluded is a helper method to define mock.On call
490+// - ctx context.Context
491+// - p domain.Principal
492+// - repo domain.RepoName
493+// - excluded bool
494+func (_e *MirrorService_Expecter) SetRepoExcluded(ctx any, p any, repo any, excluded any) *MirrorService_SetRepoExcluded_Call {
495+ return &MirrorService_SetRepoExcluded_Call{Call: _e.mock.On("SetRepoExcluded", ctx, p, repo, excluded)}
496+}
497+
498+func (_c *MirrorService_SetRepoExcluded_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, excluded bool)) *MirrorService_SetRepoExcluded_Call {
499+ _c.Call.Run(func(args mock.Arguments) {
500+ var arg0 context.Context
501+ if args[0] != nil {
502+ arg0 = args[0].(context.Context)
503+ }
504+ var arg1 domain.Principal
505+ if args[1] != nil {
506+ arg1 = args[1].(domain.Principal)
507+ }
508+ var arg2 domain.RepoName
509+ if args[2] != nil {
510+ arg2 = args[2].(domain.RepoName)
511+ }
512+ var arg3 bool
513+ if args[3] != nil {
514+ arg3 = args[3].(bool)
515+ }
516+ run(
517+ arg0,
518+ arg1,
519+ arg2,
520+ arg3,
521+ )
522+ })
523+ return _c
524+}
525+
526+func (_c *MirrorService_SetRepoExcluded_Call) Return(mirrorTarget domain.MirrorTarget, err error) *MirrorService_SetRepoExcluded_Call {
527+ _c.Call.Return(mirrorTarget, err)
528+ return _c
529+}
530+
531+func (_c *MirrorService_SetRepoExcluded_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, excluded bool) (domain.MirrorTarget, error)) *MirrorService_SetRepoExcluded_Call {
532+ _c.Call.Return(run)
533+ return _c
534+}
535+
536+// SetRepoMirror provides a mock function for the type MirrorService
537+func (_mock *MirrorService) SetRepoMirror(ctx context.Context, p domain.Principal, c domain.MirrorRepoConfig) (domain.MirrorTarget, error) {
538+ ret := _mock.Called(ctx, p, c)
539+
540+ if len(ret) == 0 {
541+ panic("no return value specified for SetRepoMirror")
542+ }
543+
544+ var r0 domain.MirrorTarget
545+ var r1 error
546+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.MirrorRepoConfig) (domain.MirrorTarget, error)); ok {
547+ return returnFunc(ctx, p, c)
548+ }
549+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.MirrorRepoConfig) domain.MirrorTarget); ok {
550+ r0 = returnFunc(ctx, p, c)
551+ } else {
552+ r0 = ret.Get(0).(domain.MirrorTarget)
553+ }
554+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.MirrorRepoConfig) error); ok {
555+ r1 = returnFunc(ctx, p, c)
556+ } else {
557+ r1 = ret.Error(1)
558+ }
559+ return r0, r1
560+}
561+
562+// MirrorService_SetRepoMirror_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetRepoMirror'
563+type MirrorService_SetRepoMirror_Call struct {
564+ *mock.Call
565+}
566+
567+// SetRepoMirror is a helper method to define mock.On call
568+// - ctx context.Context
569+// - p domain.Principal
570+// - c domain.MirrorRepoConfig
571+func (_e *MirrorService_Expecter) SetRepoMirror(ctx any, p any, c any) *MirrorService_SetRepoMirror_Call {
572+ return &MirrorService_SetRepoMirror_Call{Call: _e.mock.On("SetRepoMirror", ctx, p, c)}
573+}
574+
575+func (_c *MirrorService_SetRepoMirror_Call) Run(run func(ctx context.Context, p domain.Principal, c domain.MirrorRepoConfig)) *MirrorService_SetRepoMirror_Call {
576+ _c.Call.Run(func(args mock.Arguments) {
577+ var arg0 context.Context
578+ if args[0] != nil {
579+ arg0 = args[0].(context.Context)
580+ }
581+ var arg1 domain.Principal
582+ if args[1] != nil {
583+ arg1 = args[1].(domain.Principal)
584+ }
585+ var arg2 domain.MirrorRepoConfig
586+ if args[2] != nil {
587+ arg2 = args[2].(domain.MirrorRepoConfig)
588+ }
589+ run(
590+ arg0,
591+ arg1,
592+ arg2,
593+ )
594+ })
595+ return _c
596+}
597+
598+func (_c *MirrorService_SetRepoMirror_Call) Return(mirrorTarget domain.MirrorTarget, err error) *MirrorService_SetRepoMirror_Call {
599+ _c.Call.Return(mirrorTarget, err)
600+ return _c
601+}
602+
603+func (_c *MirrorService_SetRepoMirror_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, c domain.MirrorRepoConfig) (domain.MirrorTarget, error)) *MirrorService_SetRepoMirror_Call {
604+ _c.Call.Return(run)
605+ return _c
606+}
607+
608+// ShowKey provides a mock function for the type MirrorService
609+func (_mock *MirrorService) ShowKey(ctx context.Context, p domain.Principal) (domain.MirrorPublicKey, error) {
610+ ret := _mock.Called(ctx, p)
611+
612+ if len(ret) == 0 {
613+ panic("no return value specified for ShowKey")
614+ }
615+
616+ var r0 domain.MirrorPublicKey
617+ var r1 error
618+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) (domain.MirrorPublicKey, error)); ok {
619+ return returnFunc(ctx, p)
620+ }
621+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal) domain.MirrorPublicKey); ok {
622+ r0 = returnFunc(ctx, p)
623+ } else {
624+ r0 = ret.Get(0).(domain.MirrorPublicKey)
625+ }
626+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal) error); ok {
627+ r1 = returnFunc(ctx, p)
628+ } else {
629+ r1 = ret.Error(1)
630+ }
631+ return r0, r1
632+}
633+
634+// MirrorService_ShowKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ShowKey'
635+type MirrorService_ShowKey_Call struct {
636+ *mock.Call
637+}
638+
639+// ShowKey is a helper method to define mock.On call
640+// - ctx context.Context
641+// - p domain.Principal
642+func (_e *MirrorService_Expecter) ShowKey(ctx any, p any) *MirrorService_ShowKey_Call {
643+ return &MirrorService_ShowKey_Call{Call: _e.mock.On("ShowKey", ctx, p)}
644+}
645+
646+func (_c *MirrorService_ShowKey_Call) Run(run func(ctx context.Context, p domain.Principal)) *MirrorService_ShowKey_Call {
647+ _c.Call.Run(func(args mock.Arguments) {
648+ var arg0 context.Context
649+ if args[0] != nil {
650+ arg0 = args[0].(context.Context)
651+ }
652+ var arg1 domain.Principal
653+ if args[1] != nil {
654+ arg1 = args[1].(domain.Principal)
655+ }
656+ run(
657+ arg0,
658+ arg1,
659+ )
660+ })
661+ return _c
662+}
663+
664+func (_c *MirrorService_ShowKey_Call) Return(mirrorPublicKey domain.MirrorPublicKey, err error) *MirrorService_ShowKey_Call {
665+ _c.Call.Return(mirrorPublicKey, err)
666+ return _c
667+}
668+
669+func (_c *MirrorService_ShowKey_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal) (domain.MirrorPublicKey, error)) *MirrorService_ShowKey_Call {
670+ _c.Call.Return(run)
671+ return _c
672+}
673+
674+// Sync provides a mock function for the type MirrorService
675+func (_mock *MirrorService) Sync(ctx context.Context, p domain.Principal, repo domain.RepoName, force bool) (ports.MirrorSyncResult, error) {
676+ ret := _mock.Called(ctx, p, repo, force)
677+
678+ if len(ret) == 0 {
679+ panic("no return value specified for Sync")
680+ }
681+
682+ var r0 ports.MirrorSyncResult
683+ var r1 error
684+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, bool) (ports.MirrorSyncResult, error)); ok {
685+ return returnFunc(ctx, p, repo, force)
686+ }
687+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName, bool) ports.MirrorSyncResult); ok {
688+ r0 = returnFunc(ctx, p, repo, force)
689+ } else {
690+ r0 = ret.Get(0).(ports.MirrorSyncResult)
691+ }
692+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName, bool) error); ok {
693+ r1 = returnFunc(ctx, p, repo, force)
694+ } else {
695+ r1 = ret.Error(1)
696+ }
697+ return r0, r1
698+}
699+
700+// MirrorService_Sync_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Sync'
701+type MirrorService_Sync_Call struct {
702+ *mock.Call
703+}
704+
705+// Sync is a helper method to define mock.On call
706+// - ctx context.Context
707+// - p domain.Principal
708+// - repo domain.RepoName
709+// - force bool
710+func (_e *MirrorService_Expecter) Sync(ctx any, p any, repo any, force any) *MirrorService_Sync_Call {
711+ return &MirrorService_Sync_Call{Call: _e.mock.On("Sync", ctx, p, repo, force)}
712+}
713+
714+func (_c *MirrorService_Sync_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, force bool)) *MirrorService_Sync_Call {
715+ _c.Call.Run(func(args mock.Arguments) {
716+ var arg0 context.Context
717+ if args[0] != nil {
718+ arg0 = args[0].(context.Context)
719+ }
720+ var arg1 domain.Principal
721+ if args[1] != nil {
722+ arg1 = args[1].(domain.Principal)
723+ }
724+ var arg2 domain.RepoName
725+ if args[2] != nil {
726+ arg2 = args[2].(domain.RepoName)
727+ }
728+ var arg3 bool
729+ if args[3] != nil {
730+ arg3 = args[3].(bool)
731+ }
732+ run(
733+ arg0,
734+ arg1,
735+ arg2,
736+ arg3,
737+ )
738+ })
739+ return _c
740+}
741+
742+func (_c *MirrorService_Sync_Call) Return(mirrorSyncResult ports.MirrorSyncResult, err error) *MirrorService_Sync_Call {
743+ _c.Call.Return(mirrorSyncResult, err)
744+ return _c
745+}
746+
747+func (_c *MirrorService_Sync_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName, force bool) (ports.MirrorSyncResult, error)) *MirrorService_Sync_Call {
748+ _c.Call.Return(run)
749+ return _c
750+}
751+
752+// SyncAll provides a mock function for the type MirrorService
753+func (_mock *MirrorService) SyncAll(ctx context.Context, p domain.Principal, force bool) ([]ports.MirrorSyncResult, error) {
754+ ret := _mock.Called(ctx, p, force)
755+
756+ if len(ret) == 0 {
757+ panic("no return value specified for SyncAll")
758+ }
759+
760+ var r0 []ports.MirrorSyncResult
761+ var r1 error
762+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, bool) ([]ports.MirrorSyncResult, error)); ok {
763+ return returnFunc(ctx, p, force)
764+ }
765+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, bool) []ports.MirrorSyncResult); ok {
766+ r0 = returnFunc(ctx, p, force)
767+ } else {
768+ if ret.Get(0) != nil {
769+ r0 = ret.Get(0).([]ports.MirrorSyncResult)
770+ }
771+ }
772+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, bool) error); ok {
773+ r1 = returnFunc(ctx, p, force)
774+ } else {
775+ r1 = ret.Error(1)
776+ }
777+ return r0, r1
778+}
779+
780+// MirrorService_SyncAll_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SyncAll'
781+type MirrorService_SyncAll_Call struct {
782+ *mock.Call
783+}
784+
785+// SyncAll is a helper method to define mock.On call
786+// - ctx context.Context
787+// - p domain.Principal
788+// - force bool
789+func (_e *MirrorService_Expecter) SyncAll(ctx any, p any, force any) *MirrorService_SyncAll_Call {
790+ return &MirrorService_SyncAll_Call{Call: _e.mock.On("SyncAll", ctx, p, force)}
791+}
792+
793+func (_c *MirrorService_SyncAll_Call) Run(run func(ctx context.Context, p domain.Principal, force bool)) *MirrorService_SyncAll_Call {
794+ _c.Call.Run(func(args mock.Arguments) {
795+ var arg0 context.Context
796+ if args[0] != nil {
797+ arg0 = args[0].(context.Context)
798+ }
799+ var arg1 domain.Principal
800+ if args[1] != nil {
801+ arg1 = args[1].(domain.Principal)
802+ }
803+ var arg2 bool
804+ if args[2] != nil {
805+ arg2 = args[2].(bool)
806+ }
807+ run(
808+ arg0,
809+ arg1,
810+ arg2,
811+ )
812+ })
813+ return _c
814+}
815+
816+func (_c *MirrorService_SyncAll_Call) Return(mirrorSyncResults []ports.MirrorSyncResult, err error) *MirrorService_SyncAll_Call {
817+ _c.Call.Return(mirrorSyncResults, err)
818+ return _c
819+}
820+
821+func (_c *MirrorService_SyncAll_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, force bool) ([]ports.MirrorSyncResult, error)) *MirrorService_SyncAll_Call {
822+ _c.Call.Return(run)
823+ return _c
824+}
A internal/ports/mocks/MirrorStore.go +721 -0
@@ -0,0 +1,721 @@
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+// NewMirrorStore creates a new instance of MirrorStore. 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 NewMirrorStore(t interface {
17+ mock.TestingT
18+ Cleanup(func())
19+}) *MirrorStore {
20+ if helper, ok := t.(interface{ Helper() }); ok {
21+ helper.Helper()
22+ }
23+
24+ mock := &MirrorStore{}
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+// MirrorStore is an autogenerated mock type for the MirrorStore type
38+type MirrorStore struct {
39+ mock.Mock
40+}
41+
42+type MirrorStore_Expecter struct {
43+ mock *mock.Mock
44+}
45+
46+func (_m *MirrorStore) EXPECT() *MirrorStore_Expecter {
47+ return &MirrorStore_Expecter{mock: &_m.Mock}
48+}
49+
50+// DeleteRepoConfig provides a mock function for the type MirrorStore
51+func (_mock *MirrorStore) DeleteRepoConfig(ctx context.Context, repo domain.RepoName) error {
52+ ret := _mock.Called(ctx, repo)
53+
54+ if len(ret) == 0 {
55+ panic("no return value specified for DeleteRepoConfig")
56+ }
57+
58+ var r0 error
59+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) error); ok {
60+ r0 = returnFunc(ctx, repo)
61+ } else {
62+ r0 = ret.Error(0)
63+ }
64+ return r0
65+}
66+
67+// MirrorStore_DeleteRepoConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteRepoConfig'
68+type MirrorStore_DeleteRepoConfig_Call struct {
69+ *mock.Call
70+}
71+
72+// DeleteRepoConfig is a helper method to define mock.On call
73+// - ctx context.Context
74+// - repo domain.RepoName
75+func (_e *MirrorStore_Expecter) DeleteRepoConfig(ctx any, repo any) *MirrorStore_DeleteRepoConfig_Call {
76+ return &MirrorStore_DeleteRepoConfig_Call{Call: _e.mock.On("DeleteRepoConfig", ctx, repo)}
77+}
78+
79+func (_c *MirrorStore_DeleteRepoConfig_Call) Run(run func(ctx context.Context, repo domain.RepoName)) *MirrorStore_DeleteRepoConfig_Call {
80+ _c.Call.Run(func(args mock.Arguments) {
81+ var arg0 context.Context
82+ if args[0] != nil {
83+ arg0 = args[0].(context.Context)
84+ }
85+ var arg1 domain.RepoName
86+ if args[1] != nil {
87+ arg1 = args[1].(domain.RepoName)
88+ }
89+ run(
90+ arg0,
91+ arg1,
92+ )
93+ })
94+ return _c
95+}
96+
97+func (_c *MirrorStore_DeleteRepoConfig_Call) Return(err error) *MirrorStore_DeleteRepoConfig_Call {
98+ _c.Call.Return(err)
99+ return _c
100+}
101+
102+func (_c *MirrorStore_DeleteRepoConfig_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName) error) *MirrorStore_DeleteRepoConfig_Call {
103+ _c.Call.Return(run)
104+ return _c
105+}
106+
107+// GetGlobal provides a mock function for the type MirrorStore
108+func (_mock *MirrorStore) GetGlobal(ctx context.Context) (domain.MirrorGlobalConfig, error) {
109+ ret := _mock.Called(ctx)
110+
111+ if len(ret) == 0 {
112+ panic("no return value specified for GetGlobal")
113+ }
114+
115+ var r0 domain.MirrorGlobalConfig
116+ var r1 error
117+ if returnFunc, ok := ret.Get(0).(func(context.Context) (domain.MirrorGlobalConfig, error)); ok {
118+ return returnFunc(ctx)
119+ }
120+ if returnFunc, ok := ret.Get(0).(func(context.Context) domain.MirrorGlobalConfig); ok {
121+ r0 = returnFunc(ctx)
122+ } else {
123+ r0 = ret.Get(0).(domain.MirrorGlobalConfig)
124+ }
125+ if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok {
126+ r1 = returnFunc(ctx)
127+ } else {
128+ r1 = ret.Error(1)
129+ }
130+ return r0, r1
131+}
132+
133+// MirrorStore_GetGlobal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetGlobal'
134+type MirrorStore_GetGlobal_Call struct {
135+ *mock.Call
136+}
137+
138+// GetGlobal is a helper method to define mock.On call
139+// - ctx context.Context
140+func (_e *MirrorStore_Expecter) GetGlobal(ctx any) *MirrorStore_GetGlobal_Call {
141+ return &MirrorStore_GetGlobal_Call{Call: _e.mock.On("GetGlobal", ctx)}
142+}
143+
144+func (_c *MirrorStore_GetGlobal_Call) Run(run func(ctx context.Context)) *MirrorStore_GetGlobal_Call {
145+ _c.Call.Run(func(args mock.Arguments) {
146+ var arg0 context.Context
147+ if args[0] != nil {
148+ arg0 = args[0].(context.Context)
149+ }
150+ run(
151+ arg0,
152+ )
153+ })
154+ return _c
155+}
156+
157+func (_c *MirrorStore_GetGlobal_Call) Return(mirrorGlobalConfig domain.MirrorGlobalConfig, err error) *MirrorStore_GetGlobal_Call {
158+ _c.Call.Return(mirrorGlobalConfig, err)
159+ return _c
160+}
161+
162+func (_c *MirrorStore_GetGlobal_Call) RunAndReturn(run func(ctx context.Context) (domain.MirrorGlobalConfig, error)) *MirrorStore_GetGlobal_Call {
163+ _c.Call.Return(run)
164+ return _c
165+}
166+
167+// GetRepoConfig provides a mock function for the type MirrorStore
168+func (_mock *MirrorStore) GetRepoConfig(ctx context.Context, repo domain.RepoName) (domain.MirrorRepoConfig, error) {
169+ ret := _mock.Called(ctx, repo)
170+
171+ if len(ret) == 0 {
172+ panic("no return value specified for GetRepoConfig")
173+ }
174+
175+ var r0 domain.MirrorRepoConfig
176+ var r1 error
177+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) (domain.MirrorRepoConfig, error)); ok {
178+ return returnFunc(ctx, repo)
179+ }
180+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) domain.MirrorRepoConfig); ok {
181+ r0 = returnFunc(ctx, repo)
182+ } else {
183+ r0 = ret.Get(0).(domain.MirrorRepoConfig)
184+ }
185+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName) error); ok {
186+ r1 = returnFunc(ctx, repo)
187+ } else {
188+ r1 = ret.Error(1)
189+ }
190+ return r0, r1
191+}
192+
193+// MirrorStore_GetRepoConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRepoConfig'
194+type MirrorStore_GetRepoConfig_Call struct {
195+ *mock.Call
196+}
197+
198+// GetRepoConfig is a helper method to define mock.On call
199+// - ctx context.Context
200+// - repo domain.RepoName
201+func (_e *MirrorStore_Expecter) GetRepoConfig(ctx any, repo any) *MirrorStore_GetRepoConfig_Call {
202+ return &MirrorStore_GetRepoConfig_Call{Call: _e.mock.On("GetRepoConfig", ctx, repo)}
203+}
204+
205+func (_c *MirrorStore_GetRepoConfig_Call) Run(run func(ctx context.Context, repo domain.RepoName)) *MirrorStore_GetRepoConfig_Call {
206+ _c.Call.Run(func(args mock.Arguments) {
207+ var arg0 context.Context
208+ if args[0] != nil {
209+ arg0 = args[0].(context.Context)
210+ }
211+ var arg1 domain.RepoName
212+ if args[1] != nil {
213+ arg1 = args[1].(domain.RepoName)
214+ }
215+ run(
216+ arg0,
217+ arg1,
218+ )
219+ })
220+ return _c
221+}
222+
223+func (_c *MirrorStore_GetRepoConfig_Call) Return(mirrorRepoConfig domain.MirrorRepoConfig, err error) *MirrorStore_GetRepoConfig_Call {
224+ _c.Call.Return(mirrorRepoConfig, err)
225+ return _c
226+}
227+
228+func (_c *MirrorStore_GetRepoConfig_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName) (domain.MirrorRepoConfig, error)) *MirrorStore_GetRepoConfig_Call {
229+ _c.Call.Return(run)
230+ return _c
231+}
232+
233+// GetSnapshot provides a mock function for the type MirrorStore
234+func (_mock *MirrorStore) GetSnapshot(ctx context.Context, repo domain.RepoName) (domain.MirrorSnapshot, error) {
235+ ret := _mock.Called(ctx, repo)
236+
237+ if len(ret) == 0 {
238+ panic("no return value specified for GetSnapshot")
239+ }
240+
241+ var r0 domain.MirrorSnapshot
242+ var r1 error
243+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) (domain.MirrorSnapshot, error)); ok {
244+ return returnFunc(ctx, repo)
245+ }
246+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) domain.MirrorSnapshot); ok {
247+ r0 = returnFunc(ctx, repo)
248+ } else {
249+ r0 = ret.Get(0).(domain.MirrorSnapshot)
250+ }
251+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName) error); ok {
252+ r1 = returnFunc(ctx, repo)
253+ } else {
254+ r1 = ret.Error(1)
255+ }
256+ return r0, r1
257+}
258+
259+// MirrorStore_GetSnapshot_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSnapshot'
260+type MirrorStore_GetSnapshot_Call struct {
261+ *mock.Call
262+}
263+
264+// GetSnapshot is a helper method to define mock.On call
265+// - ctx context.Context
266+// - repo domain.RepoName
267+func (_e *MirrorStore_Expecter) GetSnapshot(ctx any, repo any) *MirrorStore_GetSnapshot_Call {
268+ return &MirrorStore_GetSnapshot_Call{Call: _e.mock.On("GetSnapshot", ctx, repo)}
269+}
270+
271+func (_c *MirrorStore_GetSnapshot_Call) Run(run func(ctx context.Context, repo domain.RepoName)) *MirrorStore_GetSnapshot_Call {
272+ _c.Call.Run(func(args mock.Arguments) {
273+ var arg0 context.Context
274+ if args[0] != nil {
275+ arg0 = args[0].(context.Context)
276+ }
277+ var arg1 domain.RepoName
278+ if args[1] != nil {
279+ arg1 = args[1].(domain.RepoName)
280+ }
281+ run(
282+ arg0,
283+ arg1,
284+ )
285+ })
286+ return _c
287+}
288+
289+func (_c *MirrorStore_GetSnapshot_Call) Return(mirrorSnapshot domain.MirrorSnapshot, err error) *MirrorStore_GetSnapshot_Call {
290+ _c.Call.Return(mirrorSnapshot, err)
291+ return _c
292+}
293+
294+func (_c *MirrorStore_GetSnapshot_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName) (domain.MirrorSnapshot, error)) *MirrorStore_GetSnapshot_Call {
295+ _c.Call.Return(run)
296+ return _c
297+}
298+
299+// GetStatus provides a mock function for the type MirrorStore
300+func (_mock *MirrorStore) GetStatus(ctx context.Context, repo domain.RepoName) (domain.MirrorStatus, error) {
301+ ret := _mock.Called(ctx, repo)
302+
303+ if len(ret) == 0 {
304+ panic("no return value specified for GetStatus")
305+ }
306+
307+ var r0 domain.MirrorStatus
308+ var r1 error
309+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) (domain.MirrorStatus, error)); ok {
310+ return returnFunc(ctx, repo)
311+ }
312+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) domain.MirrorStatus); ok {
313+ r0 = returnFunc(ctx, repo)
314+ } else {
315+ r0 = ret.Get(0).(domain.MirrorStatus)
316+ }
317+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName) error); ok {
318+ r1 = returnFunc(ctx, repo)
319+ } else {
320+ r1 = ret.Error(1)
321+ }
322+ return r0, r1
323+}
324+
325+// MirrorStore_GetStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetStatus'
326+type MirrorStore_GetStatus_Call struct {
327+ *mock.Call
328+}
329+
330+// GetStatus is a helper method to define mock.On call
331+// - ctx context.Context
332+// - repo domain.RepoName
333+func (_e *MirrorStore_Expecter) GetStatus(ctx any, repo any) *MirrorStore_GetStatus_Call {
334+ return &MirrorStore_GetStatus_Call{Call: _e.mock.On("GetStatus", ctx, repo)}
335+}
336+
337+func (_c *MirrorStore_GetStatus_Call) Run(run func(ctx context.Context, repo domain.RepoName)) *MirrorStore_GetStatus_Call {
338+ _c.Call.Run(func(args mock.Arguments) {
339+ var arg0 context.Context
340+ if args[0] != nil {
341+ arg0 = args[0].(context.Context)
342+ }
343+ var arg1 domain.RepoName
344+ if args[1] != nil {
345+ arg1 = args[1].(domain.RepoName)
346+ }
347+ run(
348+ arg0,
349+ arg1,
350+ )
351+ })
352+ return _c
353+}
354+
355+func (_c *MirrorStore_GetStatus_Call) Return(mirrorStatus domain.MirrorStatus, err error) *MirrorStore_GetStatus_Call {
356+ _c.Call.Return(mirrorStatus, err)
357+ return _c
358+}
359+
360+func (_c *MirrorStore_GetStatus_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName) (domain.MirrorStatus, error)) *MirrorStore_GetStatus_Call {
361+ _c.Call.Return(run)
362+ return _c
363+}
364+
365+// ListRepoConfigs provides a mock function for the type MirrorStore
366+func (_mock *MirrorStore) ListRepoConfigs(ctx context.Context) ([]domain.MirrorRepoConfig, error) {
367+ ret := _mock.Called(ctx)
368+
369+ if len(ret) == 0 {
370+ panic("no return value specified for ListRepoConfigs")
371+ }
372+
373+ var r0 []domain.MirrorRepoConfig
374+ var r1 error
375+ if returnFunc, ok := ret.Get(0).(func(context.Context) ([]domain.MirrorRepoConfig, error)); ok {
376+ return returnFunc(ctx)
377+ }
378+ if returnFunc, ok := ret.Get(0).(func(context.Context) []domain.MirrorRepoConfig); ok {
379+ r0 = returnFunc(ctx)
380+ } else {
381+ if ret.Get(0) != nil {
382+ r0 = ret.Get(0).([]domain.MirrorRepoConfig)
383+ }
384+ }
385+ if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok {
386+ r1 = returnFunc(ctx)
387+ } else {
388+ r1 = ret.Error(1)
389+ }
390+ return r0, r1
391+}
392+
393+// MirrorStore_ListRepoConfigs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListRepoConfigs'
394+type MirrorStore_ListRepoConfigs_Call struct {
395+ *mock.Call
396+}
397+
398+// ListRepoConfigs is a helper method to define mock.On call
399+// - ctx context.Context
400+func (_e *MirrorStore_Expecter) ListRepoConfigs(ctx any) *MirrorStore_ListRepoConfigs_Call {
401+ return &MirrorStore_ListRepoConfigs_Call{Call: _e.mock.On("ListRepoConfigs", ctx)}
402+}
403+
404+func (_c *MirrorStore_ListRepoConfigs_Call) Run(run func(ctx context.Context)) *MirrorStore_ListRepoConfigs_Call {
405+ _c.Call.Run(func(args mock.Arguments) {
406+ var arg0 context.Context
407+ if args[0] != nil {
408+ arg0 = args[0].(context.Context)
409+ }
410+ run(
411+ arg0,
412+ )
413+ })
414+ return _c
415+}
416+
417+func (_c *MirrorStore_ListRepoConfigs_Call) Return(mirrorRepoConfigs []domain.MirrorRepoConfig, err error) *MirrorStore_ListRepoConfigs_Call {
418+ _c.Call.Return(mirrorRepoConfigs, err)
419+ return _c
420+}
421+
422+func (_c *MirrorStore_ListRepoConfigs_Call) RunAndReturn(run func(ctx context.Context) ([]domain.MirrorRepoConfig, error)) *MirrorStore_ListRepoConfigs_Call {
423+ _c.Call.Return(run)
424+ return _c
425+}
426+
427+// ListStatus provides a mock function for the type MirrorStore
428+func (_mock *MirrorStore) ListStatus(ctx context.Context) ([]domain.MirrorStatus, error) {
429+ ret := _mock.Called(ctx)
430+
431+ if len(ret) == 0 {
432+ panic("no return value specified for ListStatus")
433+ }
434+
435+ var r0 []domain.MirrorStatus
436+ var r1 error
437+ if returnFunc, ok := ret.Get(0).(func(context.Context) ([]domain.MirrorStatus, error)); ok {
438+ return returnFunc(ctx)
439+ }
440+ if returnFunc, ok := ret.Get(0).(func(context.Context) []domain.MirrorStatus); ok {
441+ r0 = returnFunc(ctx)
442+ } else {
443+ if ret.Get(0) != nil {
444+ r0 = ret.Get(0).([]domain.MirrorStatus)
445+ }
446+ }
447+ if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok {
448+ r1 = returnFunc(ctx)
449+ } else {
450+ r1 = ret.Error(1)
451+ }
452+ return r0, r1
453+}
454+
455+// MirrorStore_ListStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListStatus'
456+type MirrorStore_ListStatus_Call struct {
457+ *mock.Call
458+}
459+
460+// ListStatus is a helper method to define mock.On call
461+// - ctx context.Context
462+func (_e *MirrorStore_Expecter) ListStatus(ctx any) *MirrorStore_ListStatus_Call {
463+ return &MirrorStore_ListStatus_Call{Call: _e.mock.On("ListStatus", ctx)}
464+}
465+
466+func (_c *MirrorStore_ListStatus_Call) Run(run func(ctx context.Context)) *MirrorStore_ListStatus_Call {
467+ _c.Call.Run(func(args mock.Arguments) {
468+ var arg0 context.Context
469+ if args[0] != nil {
470+ arg0 = args[0].(context.Context)
471+ }
472+ run(
473+ arg0,
474+ )
475+ })
476+ return _c
477+}
478+
479+func (_c *MirrorStore_ListStatus_Call) Return(mirrorStatuss []domain.MirrorStatus, err error) *MirrorStore_ListStatus_Call {
480+ _c.Call.Return(mirrorStatuss, err)
481+ return _c
482+}
483+
484+func (_c *MirrorStore_ListStatus_Call) RunAndReturn(run func(ctx context.Context) ([]domain.MirrorStatus, error)) *MirrorStore_ListStatus_Call {
485+ _c.Call.Return(run)
486+ return _c
487+}
488+
489+// PutGlobal provides a mock function for the type MirrorStore
490+func (_mock *MirrorStore) PutGlobal(ctx context.Context, g domain.MirrorGlobalConfig) error {
491+ ret := _mock.Called(ctx, g)
492+
493+ if len(ret) == 0 {
494+ panic("no return value specified for PutGlobal")
495+ }
496+
497+ var r0 error
498+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.MirrorGlobalConfig) error); ok {
499+ r0 = returnFunc(ctx, g)
500+ } else {
501+ r0 = ret.Error(0)
502+ }
503+ return r0
504+}
505+
506+// MirrorStore_PutGlobal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PutGlobal'
507+type MirrorStore_PutGlobal_Call struct {
508+ *mock.Call
509+}
510+
511+// PutGlobal is a helper method to define mock.On call
512+// - ctx context.Context
513+// - g domain.MirrorGlobalConfig
514+func (_e *MirrorStore_Expecter) PutGlobal(ctx any, g any) *MirrorStore_PutGlobal_Call {
515+ return &MirrorStore_PutGlobal_Call{Call: _e.mock.On("PutGlobal", ctx, g)}
516+}
517+
518+func (_c *MirrorStore_PutGlobal_Call) Run(run func(ctx context.Context, g domain.MirrorGlobalConfig)) *MirrorStore_PutGlobal_Call {
519+ _c.Call.Run(func(args mock.Arguments) {
520+ var arg0 context.Context
521+ if args[0] != nil {
522+ arg0 = args[0].(context.Context)
523+ }
524+ var arg1 domain.MirrorGlobalConfig
525+ if args[1] != nil {
526+ arg1 = args[1].(domain.MirrorGlobalConfig)
527+ }
528+ run(
529+ arg0,
530+ arg1,
531+ )
532+ })
533+ return _c
534+}
535+
536+func (_c *MirrorStore_PutGlobal_Call) Return(err error) *MirrorStore_PutGlobal_Call {
537+ _c.Call.Return(err)
538+ return _c
539+}
540+
541+func (_c *MirrorStore_PutGlobal_Call) RunAndReturn(run func(ctx context.Context, g domain.MirrorGlobalConfig) error) *MirrorStore_PutGlobal_Call {
542+ _c.Call.Return(run)
543+ return _c
544+}
545+
546+// PutRepoConfig provides a mock function for the type MirrorStore
547+func (_mock *MirrorStore) PutRepoConfig(ctx context.Context, c domain.MirrorRepoConfig) error {
548+ ret := _mock.Called(ctx, c)
549+
550+ if len(ret) == 0 {
551+ panic("no return value specified for PutRepoConfig")
552+ }
553+
554+ var r0 error
555+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.MirrorRepoConfig) error); ok {
556+ r0 = returnFunc(ctx, c)
557+ } else {
558+ r0 = ret.Error(0)
559+ }
560+ return r0
561+}
562+
563+// MirrorStore_PutRepoConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PutRepoConfig'
564+type MirrorStore_PutRepoConfig_Call struct {
565+ *mock.Call
566+}
567+
568+// PutRepoConfig is a helper method to define mock.On call
569+// - ctx context.Context
570+// - c domain.MirrorRepoConfig
571+func (_e *MirrorStore_Expecter) PutRepoConfig(ctx any, c any) *MirrorStore_PutRepoConfig_Call {
572+ return &MirrorStore_PutRepoConfig_Call{Call: _e.mock.On("PutRepoConfig", ctx, c)}
573+}
574+
575+func (_c *MirrorStore_PutRepoConfig_Call) Run(run func(ctx context.Context, c domain.MirrorRepoConfig)) *MirrorStore_PutRepoConfig_Call {
576+ _c.Call.Run(func(args mock.Arguments) {
577+ var arg0 context.Context
578+ if args[0] != nil {
579+ arg0 = args[0].(context.Context)
580+ }
581+ var arg1 domain.MirrorRepoConfig
582+ if args[1] != nil {
583+ arg1 = args[1].(domain.MirrorRepoConfig)
584+ }
585+ run(
586+ arg0,
587+ arg1,
588+ )
589+ })
590+ return _c
591+}
592+
593+func (_c *MirrorStore_PutRepoConfig_Call) Return(err error) *MirrorStore_PutRepoConfig_Call {
594+ _c.Call.Return(err)
595+ return _c
596+}
597+
598+func (_c *MirrorStore_PutRepoConfig_Call) RunAndReturn(run func(ctx context.Context, c domain.MirrorRepoConfig) error) *MirrorStore_PutRepoConfig_Call {
599+ _c.Call.Return(run)
600+ return _c
601+}
602+
603+// PutSnapshot provides a mock function for the type MirrorStore
604+func (_mock *MirrorStore) PutSnapshot(ctx context.Context, repo domain.RepoName, s domain.MirrorSnapshot) error {
605+ ret := _mock.Called(ctx, repo, s)
606+
607+ if len(ret) == 0 {
608+ panic("no return value specified for PutSnapshot")
609+ }
610+
611+ var r0 error
612+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.MirrorSnapshot) error); ok {
613+ r0 = returnFunc(ctx, repo, s)
614+ } else {
615+ r0 = ret.Error(0)
616+ }
617+ return r0
618+}
619+
620+// MirrorStore_PutSnapshot_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PutSnapshot'
621+type MirrorStore_PutSnapshot_Call struct {
622+ *mock.Call
623+}
624+
625+// PutSnapshot is a helper method to define mock.On call
626+// - ctx context.Context
627+// - repo domain.RepoName
628+// - s domain.MirrorSnapshot
629+func (_e *MirrorStore_Expecter) PutSnapshot(ctx any, repo any, s any) *MirrorStore_PutSnapshot_Call {
630+ return &MirrorStore_PutSnapshot_Call{Call: _e.mock.On("PutSnapshot", ctx, repo, s)}
631+}
632+
633+func (_c *MirrorStore_PutSnapshot_Call) Run(run func(ctx context.Context, repo domain.RepoName, s domain.MirrorSnapshot)) *MirrorStore_PutSnapshot_Call {
634+ _c.Call.Run(func(args mock.Arguments) {
635+ var arg0 context.Context
636+ if args[0] != nil {
637+ arg0 = args[0].(context.Context)
638+ }
639+ var arg1 domain.RepoName
640+ if args[1] != nil {
641+ arg1 = args[1].(domain.RepoName)
642+ }
643+ var arg2 domain.MirrorSnapshot
644+ if args[2] != nil {
645+ arg2 = args[2].(domain.MirrorSnapshot)
646+ }
647+ run(
648+ arg0,
649+ arg1,
650+ arg2,
651+ )
652+ })
653+ return _c
654+}
655+
656+func (_c *MirrorStore_PutSnapshot_Call) Return(err error) *MirrorStore_PutSnapshot_Call {
657+ _c.Call.Return(err)
658+ return _c
659+}
660+
661+func (_c *MirrorStore_PutSnapshot_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, s domain.MirrorSnapshot) error) *MirrorStore_PutSnapshot_Call {
662+ _c.Call.Return(run)
663+ return _c
664+}
665+
666+// PutStatus provides a mock function for the type MirrorStore
667+func (_mock *MirrorStore) PutStatus(ctx context.Context, s domain.MirrorStatus) error {
668+ ret := _mock.Called(ctx, s)
669+
670+ if len(ret) == 0 {
671+ panic("no return value specified for PutStatus")
672+ }
673+
674+ var r0 error
675+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.MirrorStatus) error); ok {
676+ r0 = returnFunc(ctx, s)
677+ } else {
678+ r0 = ret.Error(0)
679+ }
680+ return r0
681+}
682+
683+// MirrorStore_PutStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PutStatus'
684+type MirrorStore_PutStatus_Call struct {
685+ *mock.Call
686+}
687+
688+// PutStatus is a helper method to define mock.On call
689+// - ctx context.Context
690+// - s domain.MirrorStatus
691+func (_e *MirrorStore_Expecter) PutStatus(ctx any, s any) *MirrorStore_PutStatus_Call {
692+ return &MirrorStore_PutStatus_Call{Call: _e.mock.On("PutStatus", ctx, s)}
693+}
694+
695+func (_c *MirrorStore_PutStatus_Call) Run(run func(ctx context.Context, s domain.MirrorStatus)) *MirrorStore_PutStatus_Call {
696+ _c.Call.Run(func(args mock.Arguments) {
697+ var arg0 context.Context
698+ if args[0] != nil {
699+ arg0 = args[0].(context.Context)
700+ }
701+ var arg1 domain.MirrorStatus
702+ if args[1] != nil {
703+ arg1 = args[1].(domain.MirrorStatus)
704+ }
705+ run(
706+ arg0,
707+ arg1,
708+ )
709+ })
710+ return _c
711+}
712+
713+func (_c *MirrorStore_PutStatus_Call) Return(err error) *MirrorStore_PutStatus_Call {
714+ _c.Call.Return(err)
715+ return _c
716+}
717+
718+func (_c *MirrorStore_PutStatus_Call) RunAndReturn(run func(ctx context.Context, s domain.MirrorStatus) error) *MirrorStore_PutStatus_Call {
719+ _c.Call.Return(run)
720+ return _c
721+}
@@ -0,0 +1,721 @@
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+// NewMirrorStore creates a new instance of MirrorStore. 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 NewMirrorStore(t interface {
17+ mock.TestingT
18+ Cleanup(func())
19+}) *MirrorStore {
20+ if helper, ok := t.(interface{ Helper() }); ok {
21+ helper.Helper()
22+ }
23+
24+ mock := &MirrorStore{}
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+// MirrorStore is an autogenerated mock type for the MirrorStore type
38+type MirrorStore struct {
39+ mock.Mock
40+}
41+
42+type MirrorStore_Expecter struct {
43+ mock *mock.Mock
44+}
45+
46+func (_m *MirrorStore) EXPECT() *MirrorStore_Expecter {
47+ return &MirrorStore_Expecter{mock: &_m.Mock}
48+}
49+
50+// DeleteRepoConfig provides a mock function for the type MirrorStore
51+func (_mock *MirrorStore) DeleteRepoConfig(ctx context.Context, repo domain.RepoName) error {
52+ ret := _mock.Called(ctx, repo)
53+
54+ if len(ret) == 0 {
55+ panic("no return value specified for DeleteRepoConfig")
56+ }
57+
58+ var r0 error
59+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) error); ok {
60+ r0 = returnFunc(ctx, repo)
61+ } else {
62+ r0 = ret.Error(0)
63+ }
64+ return r0
65+}
66+
67+// MirrorStore_DeleteRepoConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteRepoConfig'
68+type MirrorStore_DeleteRepoConfig_Call struct {
69+ *mock.Call
70+}
71+
72+// DeleteRepoConfig is a helper method to define mock.On call
73+// - ctx context.Context
74+// - repo domain.RepoName
75+func (_e *MirrorStore_Expecter) DeleteRepoConfig(ctx any, repo any) *MirrorStore_DeleteRepoConfig_Call {
76+ return &MirrorStore_DeleteRepoConfig_Call{Call: _e.mock.On("DeleteRepoConfig", ctx, repo)}
77+}
78+
79+func (_c *MirrorStore_DeleteRepoConfig_Call) Run(run func(ctx context.Context, repo domain.RepoName)) *MirrorStore_DeleteRepoConfig_Call {
80+ _c.Call.Run(func(args mock.Arguments) {
81+ var arg0 context.Context
82+ if args[0] != nil {
83+ arg0 = args[0].(context.Context)
84+ }
85+ var arg1 domain.RepoName
86+ if args[1] != nil {
87+ arg1 = args[1].(domain.RepoName)
88+ }
89+ run(
90+ arg0,
91+ arg1,
92+ )
93+ })
94+ return _c
95+}
96+
97+func (_c *MirrorStore_DeleteRepoConfig_Call) Return(err error) *MirrorStore_DeleteRepoConfig_Call {
98+ _c.Call.Return(err)
99+ return _c
100+}
101+
102+func (_c *MirrorStore_DeleteRepoConfig_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName) error) *MirrorStore_DeleteRepoConfig_Call {
103+ _c.Call.Return(run)
104+ return _c
105+}
106+
107+// GetGlobal provides a mock function for the type MirrorStore
108+func (_mock *MirrorStore) GetGlobal(ctx context.Context) (domain.MirrorGlobalConfig, error) {
109+ ret := _mock.Called(ctx)
110+
111+ if len(ret) == 0 {
112+ panic("no return value specified for GetGlobal")
113+ }
114+
115+ var r0 domain.MirrorGlobalConfig
116+ var r1 error
117+ if returnFunc, ok := ret.Get(0).(func(context.Context) (domain.MirrorGlobalConfig, error)); ok {
118+ return returnFunc(ctx)
119+ }
120+ if returnFunc, ok := ret.Get(0).(func(context.Context) domain.MirrorGlobalConfig); ok {
121+ r0 = returnFunc(ctx)
122+ } else {
123+ r0 = ret.Get(0).(domain.MirrorGlobalConfig)
124+ }
125+ if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok {
126+ r1 = returnFunc(ctx)
127+ } else {
128+ r1 = ret.Error(1)
129+ }
130+ return r0, r1
131+}
132+
133+// MirrorStore_GetGlobal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetGlobal'
134+type MirrorStore_GetGlobal_Call struct {
135+ *mock.Call
136+}
137+
138+// GetGlobal is a helper method to define mock.On call
139+// - ctx context.Context
140+func (_e *MirrorStore_Expecter) GetGlobal(ctx any) *MirrorStore_GetGlobal_Call {
141+ return &MirrorStore_GetGlobal_Call{Call: _e.mock.On("GetGlobal", ctx)}
142+}
143+
144+func (_c *MirrorStore_GetGlobal_Call) Run(run func(ctx context.Context)) *MirrorStore_GetGlobal_Call {
145+ _c.Call.Run(func(args mock.Arguments) {
146+ var arg0 context.Context
147+ if args[0] != nil {
148+ arg0 = args[0].(context.Context)
149+ }
150+ run(
151+ arg0,
152+ )
153+ })
154+ return _c
155+}
156+
157+func (_c *MirrorStore_GetGlobal_Call) Return(mirrorGlobalConfig domain.MirrorGlobalConfig, err error) *MirrorStore_GetGlobal_Call {
158+ _c.Call.Return(mirrorGlobalConfig, err)
159+ return _c
160+}
161+
162+func (_c *MirrorStore_GetGlobal_Call) RunAndReturn(run func(ctx context.Context) (domain.MirrorGlobalConfig, error)) *MirrorStore_GetGlobal_Call {
163+ _c.Call.Return(run)
164+ return _c
165+}
166+
167+// GetRepoConfig provides a mock function for the type MirrorStore
168+func (_mock *MirrorStore) GetRepoConfig(ctx context.Context, repo domain.RepoName) (domain.MirrorRepoConfig, error) {
169+ ret := _mock.Called(ctx, repo)
170+
171+ if len(ret) == 0 {
172+ panic("no return value specified for GetRepoConfig")
173+ }
174+
175+ var r0 domain.MirrorRepoConfig
176+ var r1 error
177+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) (domain.MirrorRepoConfig, error)); ok {
178+ return returnFunc(ctx, repo)
179+ }
180+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) domain.MirrorRepoConfig); ok {
181+ r0 = returnFunc(ctx, repo)
182+ } else {
183+ r0 = ret.Get(0).(domain.MirrorRepoConfig)
184+ }
185+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName) error); ok {
186+ r1 = returnFunc(ctx, repo)
187+ } else {
188+ r1 = ret.Error(1)
189+ }
190+ return r0, r1
191+}
192+
193+// MirrorStore_GetRepoConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRepoConfig'
194+type MirrorStore_GetRepoConfig_Call struct {
195+ *mock.Call
196+}
197+
198+// GetRepoConfig is a helper method to define mock.On call
199+// - ctx context.Context
200+// - repo domain.RepoName
201+func (_e *MirrorStore_Expecter) GetRepoConfig(ctx any, repo any) *MirrorStore_GetRepoConfig_Call {
202+ return &MirrorStore_GetRepoConfig_Call{Call: _e.mock.On("GetRepoConfig", ctx, repo)}
203+}
204+
205+func (_c *MirrorStore_GetRepoConfig_Call) Run(run func(ctx context.Context, repo domain.RepoName)) *MirrorStore_GetRepoConfig_Call {
206+ _c.Call.Run(func(args mock.Arguments) {
207+ var arg0 context.Context
208+ if args[0] != nil {
209+ arg0 = args[0].(context.Context)
210+ }
211+ var arg1 domain.RepoName
212+ if args[1] != nil {
213+ arg1 = args[1].(domain.RepoName)
214+ }
215+ run(
216+ arg0,
217+ arg1,
218+ )
219+ })
220+ return _c
221+}
222+
223+func (_c *MirrorStore_GetRepoConfig_Call) Return(mirrorRepoConfig domain.MirrorRepoConfig, err error) *MirrorStore_GetRepoConfig_Call {
224+ _c.Call.Return(mirrorRepoConfig, err)
225+ return _c
226+}
227+
228+func (_c *MirrorStore_GetRepoConfig_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName) (domain.MirrorRepoConfig, error)) *MirrorStore_GetRepoConfig_Call {
229+ _c.Call.Return(run)
230+ return _c
231+}
232+
233+// GetSnapshot provides a mock function for the type MirrorStore
234+func (_mock *MirrorStore) GetSnapshot(ctx context.Context, repo domain.RepoName) (domain.MirrorSnapshot, error) {
235+ ret := _mock.Called(ctx, repo)
236+
237+ if len(ret) == 0 {
238+ panic("no return value specified for GetSnapshot")
239+ }
240+
241+ var r0 domain.MirrorSnapshot
242+ var r1 error
243+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) (domain.MirrorSnapshot, error)); ok {
244+ return returnFunc(ctx, repo)
245+ }
246+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) domain.MirrorSnapshot); ok {
247+ r0 = returnFunc(ctx, repo)
248+ } else {
249+ r0 = ret.Get(0).(domain.MirrorSnapshot)
250+ }
251+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName) error); ok {
252+ r1 = returnFunc(ctx, repo)
253+ } else {
254+ r1 = ret.Error(1)
255+ }
256+ return r0, r1
257+}
258+
259+// MirrorStore_GetSnapshot_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSnapshot'
260+type MirrorStore_GetSnapshot_Call struct {
261+ *mock.Call
262+}
263+
264+// GetSnapshot is a helper method to define mock.On call
265+// - ctx context.Context
266+// - repo domain.RepoName
267+func (_e *MirrorStore_Expecter) GetSnapshot(ctx any, repo any) *MirrorStore_GetSnapshot_Call {
268+ return &MirrorStore_GetSnapshot_Call{Call: _e.mock.On("GetSnapshot", ctx, repo)}
269+}
270+
271+func (_c *MirrorStore_GetSnapshot_Call) Run(run func(ctx context.Context, repo domain.RepoName)) *MirrorStore_GetSnapshot_Call {
272+ _c.Call.Run(func(args mock.Arguments) {
273+ var arg0 context.Context
274+ if args[0] != nil {
275+ arg0 = args[0].(context.Context)
276+ }
277+ var arg1 domain.RepoName
278+ if args[1] != nil {
279+ arg1 = args[1].(domain.RepoName)
280+ }
281+ run(
282+ arg0,
283+ arg1,
284+ )
285+ })
286+ return _c
287+}
288+
289+func (_c *MirrorStore_GetSnapshot_Call) Return(mirrorSnapshot domain.MirrorSnapshot, err error) *MirrorStore_GetSnapshot_Call {
290+ _c.Call.Return(mirrorSnapshot, err)
291+ return _c
292+}
293+
294+func (_c *MirrorStore_GetSnapshot_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName) (domain.MirrorSnapshot, error)) *MirrorStore_GetSnapshot_Call {
295+ _c.Call.Return(run)
296+ return _c
297+}
298+
299+// GetStatus provides a mock function for the type MirrorStore
300+func (_mock *MirrorStore) GetStatus(ctx context.Context, repo domain.RepoName) (domain.MirrorStatus, error) {
301+ ret := _mock.Called(ctx, repo)
302+
303+ if len(ret) == 0 {
304+ panic("no return value specified for GetStatus")
305+ }
306+
307+ var r0 domain.MirrorStatus
308+ var r1 error
309+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) (domain.MirrorStatus, error)); ok {
310+ return returnFunc(ctx, repo)
311+ }
312+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) domain.MirrorStatus); ok {
313+ r0 = returnFunc(ctx, repo)
314+ } else {
315+ r0 = ret.Get(0).(domain.MirrorStatus)
316+ }
317+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName) error); ok {
318+ r1 = returnFunc(ctx, repo)
319+ } else {
320+ r1 = ret.Error(1)
321+ }
322+ return r0, r1
323+}
324+
325+// MirrorStore_GetStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetStatus'
326+type MirrorStore_GetStatus_Call struct {
327+ *mock.Call
328+}
329+
330+// GetStatus is a helper method to define mock.On call
331+// - ctx context.Context
332+// - repo domain.RepoName
333+func (_e *MirrorStore_Expecter) GetStatus(ctx any, repo any) *MirrorStore_GetStatus_Call {
334+ return &MirrorStore_GetStatus_Call{Call: _e.mock.On("GetStatus", ctx, repo)}
335+}
336+
337+func (_c *MirrorStore_GetStatus_Call) Run(run func(ctx context.Context, repo domain.RepoName)) *MirrorStore_GetStatus_Call {
338+ _c.Call.Run(func(args mock.Arguments) {
339+ var arg0 context.Context
340+ if args[0] != nil {
341+ arg0 = args[0].(context.Context)
342+ }
343+ var arg1 domain.RepoName
344+ if args[1] != nil {
345+ arg1 = args[1].(domain.RepoName)
346+ }
347+ run(
348+ arg0,
349+ arg1,
350+ )
351+ })
352+ return _c
353+}
354+
355+func (_c *MirrorStore_GetStatus_Call) Return(mirrorStatus domain.MirrorStatus, err error) *MirrorStore_GetStatus_Call {
356+ _c.Call.Return(mirrorStatus, err)
357+ return _c
358+}
359+
360+func (_c *MirrorStore_GetStatus_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName) (domain.MirrorStatus, error)) *MirrorStore_GetStatus_Call {
361+ _c.Call.Return(run)
362+ return _c
363+}
364+
365+// ListRepoConfigs provides a mock function for the type MirrorStore
366+func (_mock *MirrorStore) ListRepoConfigs(ctx context.Context) ([]domain.MirrorRepoConfig, error) {
367+ ret := _mock.Called(ctx)
368+
369+ if len(ret) == 0 {
370+ panic("no return value specified for ListRepoConfigs")
371+ }
372+
373+ var r0 []domain.MirrorRepoConfig
374+ var r1 error
375+ if returnFunc, ok := ret.Get(0).(func(context.Context) ([]domain.MirrorRepoConfig, error)); ok {
376+ return returnFunc(ctx)
377+ }
378+ if returnFunc, ok := ret.Get(0).(func(context.Context) []domain.MirrorRepoConfig); ok {
379+ r0 = returnFunc(ctx)
380+ } else {
381+ if ret.Get(0) != nil {
382+ r0 = ret.Get(0).([]domain.MirrorRepoConfig)
383+ }
384+ }
385+ if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok {
386+ r1 = returnFunc(ctx)
387+ } else {
388+ r1 = ret.Error(1)
389+ }
390+ return r0, r1
391+}
392+
393+// MirrorStore_ListRepoConfigs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListRepoConfigs'
394+type MirrorStore_ListRepoConfigs_Call struct {
395+ *mock.Call
396+}
397+
398+// ListRepoConfigs is a helper method to define mock.On call
399+// - ctx context.Context
400+func (_e *MirrorStore_Expecter) ListRepoConfigs(ctx any) *MirrorStore_ListRepoConfigs_Call {
401+ return &MirrorStore_ListRepoConfigs_Call{Call: _e.mock.On("ListRepoConfigs", ctx)}
402+}
403+
404+func (_c *MirrorStore_ListRepoConfigs_Call) Run(run func(ctx context.Context)) *MirrorStore_ListRepoConfigs_Call {
405+ _c.Call.Run(func(args mock.Arguments) {
406+ var arg0 context.Context
407+ if args[0] != nil {
408+ arg0 = args[0].(context.Context)
409+ }
410+ run(
411+ arg0,
412+ )
413+ })
414+ return _c
415+}
416+
417+func (_c *MirrorStore_ListRepoConfigs_Call) Return(mirrorRepoConfigs []domain.MirrorRepoConfig, err error) *MirrorStore_ListRepoConfigs_Call {
418+ _c.Call.Return(mirrorRepoConfigs, err)
419+ return _c
420+}
421+
422+func (_c *MirrorStore_ListRepoConfigs_Call) RunAndReturn(run func(ctx context.Context) ([]domain.MirrorRepoConfig, error)) *MirrorStore_ListRepoConfigs_Call {
423+ _c.Call.Return(run)
424+ return _c
425+}
426+
427+// ListStatus provides a mock function for the type MirrorStore
428+func (_mock *MirrorStore) ListStatus(ctx context.Context) ([]domain.MirrorStatus, error) {
429+ ret := _mock.Called(ctx)
430+
431+ if len(ret) == 0 {
432+ panic("no return value specified for ListStatus")
433+ }
434+
435+ var r0 []domain.MirrorStatus
436+ var r1 error
437+ if returnFunc, ok := ret.Get(0).(func(context.Context) ([]domain.MirrorStatus, error)); ok {
438+ return returnFunc(ctx)
439+ }
440+ if returnFunc, ok := ret.Get(0).(func(context.Context) []domain.MirrorStatus); ok {
441+ r0 = returnFunc(ctx)
442+ } else {
443+ if ret.Get(0) != nil {
444+ r0 = ret.Get(0).([]domain.MirrorStatus)
445+ }
446+ }
447+ if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok {
448+ r1 = returnFunc(ctx)
449+ } else {
450+ r1 = ret.Error(1)
451+ }
452+ return r0, r1
453+}
454+
455+// MirrorStore_ListStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListStatus'
456+type MirrorStore_ListStatus_Call struct {
457+ *mock.Call
458+}
459+
460+// ListStatus is a helper method to define mock.On call
461+// - ctx context.Context
462+func (_e *MirrorStore_Expecter) ListStatus(ctx any) *MirrorStore_ListStatus_Call {
463+ return &MirrorStore_ListStatus_Call{Call: _e.mock.On("ListStatus", ctx)}
464+}
465+
466+func (_c *MirrorStore_ListStatus_Call) Run(run func(ctx context.Context)) *MirrorStore_ListStatus_Call {
467+ _c.Call.Run(func(args mock.Arguments) {
468+ var arg0 context.Context
469+ if args[0] != nil {
470+ arg0 = args[0].(context.Context)
471+ }
472+ run(
473+ arg0,
474+ )
475+ })
476+ return _c
477+}
478+
479+func (_c *MirrorStore_ListStatus_Call) Return(mirrorStatuss []domain.MirrorStatus, err error) *MirrorStore_ListStatus_Call {
480+ _c.Call.Return(mirrorStatuss, err)
481+ return _c
482+}
483+
484+func (_c *MirrorStore_ListStatus_Call) RunAndReturn(run func(ctx context.Context) ([]domain.MirrorStatus, error)) *MirrorStore_ListStatus_Call {
485+ _c.Call.Return(run)
486+ return _c
487+}
488+
489+// PutGlobal provides a mock function for the type MirrorStore
490+func (_mock *MirrorStore) PutGlobal(ctx context.Context, g domain.MirrorGlobalConfig) error {
491+ ret := _mock.Called(ctx, g)
492+
493+ if len(ret) == 0 {
494+ panic("no return value specified for PutGlobal")
495+ }
496+
497+ var r0 error
498+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.MirrorGlobalConfig) error); ok {
499+ r0 = returnFunc(ctx, g)
500+ } else {
501+ r0 = ret.Error(0)
502+ }
503+ return r0
504+}
505+
506+// MirrorStore_PutGlobal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PutGlobal'
507+type MirrorStore_PutGlobal_Call struct {
508+ *mock.Call
509+}
510+
511+// PutGlobal is a helper method to define mock.On call
512+// - ctx context.Context
513+// - g domain.MirrorGlobalConfig
514+func (_e *MirrorStore_Expecter) PutGlobal(ctx any, g any) *MirrorStore_PutGlobal_Call {
515+ return &MirrorStore_PutGlobal_Call{Call: _e.mock.On("PutGlobal", ctx, g)}
516+}
517+
518+func (_c *MirrorStore_PutGlobal_Call) Run(run func(ctx context.Context, g domain.MirrorGlobalConfig)) *MirrorStore_PutGlobal_Call {
519+ _c.Call.Run(func(args mock.Arguments) {
520+ var arg0 context.Context
521+ if args[0] != nil {
522+ arg0 = args[0].(context.Context)
523+ }
524+ var arg1 domain.MirrorGlobalConfig
525+ if args[1] != nil {
526+ arg1 = args[1].(domain.MirrorGlobalConfig)
527+ }
528+ run(
529+ arg0,
530+ arg1,
531+ )
532+ })
533+ return _c
534+}
535+
536+func (_c *MirrorStore_PutGlobal_Call) Return(err error) *MirrorStore_PutGlobal_Call {
537+ _c.Call.Return(err)
538+ return _c
539+}
540+
541+func (_c *MirrorStore_PutGlobal_Call) RunAndReturn(run func(ctx context.Context, g domain.MirrorGlobalConfig) error) *MirrorStore_PutGlobal_Call {
542+ _c.Call.Return(run)
543+ return _c
544+}
545+
546+// PutRepoConfig provides a mock function for the type MirrorStore
547+func (_mock *MirrorStore) PutRepoConfig(ctx context.Context, c domain.MirrorRepoConfig) error {
548+ ret := _mock.Called(ctx, c)
549+
550+ if len(ret) == 0 {
551+ panic("no return value specified for PutRepoConfig")
552+ }
553+
554+ var r0 error
555+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.MirrorRepoConfig) error); ok {
556+ r0 = returnFunc(ctx, c)
557+ } else {
558+ r0 = ret.Error(0)
559+ }
560+ return r0
561+}
562+
563+// MirrorStore_PutRepoConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PutRepoConfig'
564+type MirrorStore_PutRepoConfig_Call struct {
565+ *mock.Call
566+}
567+
568+// PutRepoConfig is a helper method to define mock.On call
569+// - ctx context.Context
570+// - c domain.MirrorRepoConfig
571+func (_e *MirrorStore_Expecter) PutRepoConfig(ctx any, c any) *MirrorStore_PutRepoConfig_Call {
572+ return &MirrorStore_PutRepoConfig_Call{Call: _e.mock.On("PutRepoConfig", ctx, c)}
573+}
574+
575+func (_c *MirrorStore_PutRepoConfig_Call) Run(run func(ctx context.Context, c domain.MirrorRepoConfig)) *MirrorStore_PutRepoConfig_Call {
576+ _c.Call.Run(func(args mock.Arguments) {
577+ var arg0 context.Context
578+ if args[0] != nil {
579+ arg0 = args[0].(context.Context)
580+ }
581+ var arg1 domain.MirrorRepoConfig
582+ if args[1] != nil {
583+ arg1 = args[1].(domain.MirrorRepoConfig)
584+ }
585+ run(
586+ arg0,
587+ arg1,
588+ )
589+ })
590+ return _c
591+}
592+
593+func (_c *MirrorStore_PutRepoConfig_Call) Return(err error) *MirrorStore_PutRepoConfig_Call {
594+ _c.Call.Return(err)
595+ return _c
596+}
597+
598+func (_c *MirrorStore_PutRepoConfig_Call) RunAndReturn(run func(ctx context.Context, c domain.MirrorRepoConfig) error) *MirrorStore_PutRepoConfig_Call {
599+ _c.Call.Return(run)
600+ return _c
601+}
602+
603+// PutSnapshot provides a mock function for the type MirrorStore
604+func (_mock *MirrorStore) PutSnapshot(ctx context.Context, repo domain.RepoName, s domain.MirrorSnapshot) error {
605+ ret := _mock.Called(ctx, repo, s)
606+
607+ if len(ret) == 0 {
608+ panic("no return value specified for PutSnapshot")
609+ }
610+
611+ var r0 error
612+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.MirrorSnapshot) error); ok {
613+ r0 = returnFunc(ctx, repo, s)
614+ } else {
615+ r0 = ret.Error(0)
616+ }
617+ return r0
618+}
619+
620+// MirrorStore_PutSnapshot_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PutSnapshot'
621+type MirrorStore_PutSnapshot_Call struct {
622+ *mock.Call
623+}
624+
625+// PutSnapshot is a helper method to define mock.On call
626+// - ctx context.Context
627+// - repo domain.RepoName
628+// - s domain.MirrorSnapshot
629+func (_e *MirrorStore_Expecter) PutSnapshot(ctx any, repo any, s any) *MirrorStore_PutSnapshot_Call {
630+ return &MirrorStore_PutSnapshot_Call{Call: _e.mock.On("PutSnapshot", ctx, repo, s)}
631+}
632+
633+func (_c *MirrorStore_PutSnapshot_Call) Run(run func(ctx context.Context, repo domain.RepoName, s domain.MirrorSnapshot)) *MirrorStore_PutSnapshot_Call {
634+ _c.Call.Run(func(args mock.Arguments) {
635+ var arg0 context.Context
636+ if args[0] != nil {
637+ arg0 = args[0].(context.Context)
638+ }
639+ var arg1 domain.RepoName
640+ if args[1] != nil {
641+ arg1 = args[1].(domain.RepoName)
642+ }
643+ var arg2 domain.MirrorSnapshot
644+ if args[2] != nil {
645+ arg2 = args[2].(domain.MirrorSnapshot)
646+ }
647+ run(
648+ arg0,
649+ arg1,
650+ arg2,
651+ )
652+ })
653+ return _c
654+}
655+
656+func (_c *MirrorStore_PutSnapshot_Call) Return(err error) *MirrorStore_PutSnapshot_Call {
657+ _c.Call.Return(err)
658+ return _c
659+}
660+
661+func (_c *MirrorStore_PutSnapshot_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, s domain.MirrorSnapshot) error) *MirrorStore_PutSnapshot_Call {
662+ _c.Call.Return(run)
663+ return _c
664+}
665+
666+// PutStatus provides a mock function for the type MirrorStore
667+func (_mock *MirrorStore) PutStatus(ctx context.Context, s domain.MirrorStatus) error {
668+ ret := _mock.Called(ctx, s)
669+
670+ if len(ret) == 0 {
671+ panic("no return value specified for PutStatus")
672+ }
673+
674+ var r0 error
675+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.MirrorStatus) error); ok {
676+ r0 = returnFunc(ctx, s)
677+ } else {
678+ r0 = ret.Error(0)
679+ }
680+ return r0
681+}
682+
683+// MirrorStore_PutStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PutStatus'
684+type MirrorStore_PutStatus_Call struct {
685+ *mock.Call
686+}
687+
688+// PutStatus is a helper method to define mock.On call
689+// - ctx context.Context
690+// - s domain.MirrorStatus
691+func (_e *MirrorStore_Expecter) PutStatus(ctx any, s any) *MirrorStore_PutStatus_Call {
692+ return &MirrorStore_PutStatus_Call{Call: _e.mock.On("PutStatus", ctx, s)}
693+}
694+
695+func (_c *MirrorStore_PutStatus_Call) Run(run func(ctx context.Context, s domain.MirrorStatus)) *MirrorStore_PutStatus_Call {
696+ _c.Call.Run(func(args mock.Arguments) {
697+ var arg0 context.Context
698+ if args[0] != nil {
699+ arg0 = args[0].(context.Context)
700+ }
701+ var arg1 domain.MirrorStatus
702+ if args[1] != nil {
703+ arg1 = args[1].(domain.MirrorStatus)
704+ }
705+ run(
706+ arg0,
707+ arg1,
708+ )
709+ })
710+ return _c
711+}
712+
713+func (_c *MirrorStore_PutStatus_Call) Return(err error) *MirrorStore_PutStatus_Call {
714+ _c.Call.Return(err)
715+ return _c
716+}
717+
718+func (_c *MirrorStore_PutStatus_Call) RunAndReturn(run func(ctx context.Context, s domain.MirrorStatus) error) *MirrorStore_PutStatus_Call {
719+ _c.Call.Return(run)
720+ return _c
721+}
A internal/ports/mocks/MirrorTrigger.go +100 -0
@@ -0,0 +1,100 @@
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+// NewMirrorTrigger creates a new instance of MirrorTrigger. 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 NewMirrorTrigger(t interface {
17+ mock.TestingT
18+ Cleanup(func())
19+}) *MirrorTrigger {
20+ if helper, ok := t.(interface{ Helper() }); ok {
21+ helper.Helper()
22+ }
23+
24+ mock := &MirrorTrigger{}
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+// MirrorTrigger is an autogenerated mock type for the MirrorTrigger type
38+type MirrorTrigger struct {
39+ mock.Mock
40+}
41+
42+type MirrorTrigger_Expecter struct {
43+ mock *mock.Mock
44+}
45+
46+func (_m *MirrorTrigger) EXPECT() *MirrorTrigger_Expecter {
47+ return &MirrorTrigger_Expecter{mock: &_m.Mock}
48+}
49+
50+// OnPush provides a mock function for the type MirrorTrigger
51+func (_mock *MirrorTrigger) OnPush(ctx context.Context, repo domain.RepoName, updates []domain.RefUpdate) {
52+ _mock.Called(ctx, repo, updates)
53+ return
54+}
55+
56+// MirrorTrigger_OnPush_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OnPush'
57+type MirrorTrigger_OnPush_Call struct {
58+ *mock.Call
59+}
60+
61+// OnPush is a helper method to define mock.On call
62+// - ctx context.Context
63+// - repo domain.RepoName
64+// - updates []domain.RefUpdate
65+func (_e *MirrorTrigger_Expecter) OnPush(ctx any, repo any, updates any) *MirrorTrigger_OnPush_Call {
66+ return &MirrorTrigger_OnPush_Call{Call: _e.mock.On("OnPush", ctx, repo, updates)}
67+}
68+
69+func (_c *MirrorTrigger_OnPush_Call) Run(run func(ctx context.Context, repo domain.RepoName, updates []domain.RefUpdate)) *MirrorTrigger_OnPush_Call {
70+ _c.Call.Run(func(args mock.Arguments) {
71+ var arg0 context.Context
72+ if args[0] != nil {
73+ arg0 = args[0].(context.Context)
74+ }
75+ var arg1 domain.RepoName
76+ if args[1] != nil {
77+ arg1 = args[1].(domain.RepoName)
78+ }
79+ var arg2 []domain.RefUpdate
80+ if args[2] != nil {
81+ arg2 = args[2].([]domain.RefUpdate)
82+ }
83+ run(
84+ arg0,
85+ arg1,
86+ arg2,
87+ )
88+ })
89+ return _c
90+}
91+
92+func (_c *MirrorTrigger_OnPush_Call) Return() *MirrorTrigger_OnPush_Call {
93+ _c.Call.Return()
94+ return _c
95+}
96+
97+func (_c *MirrorTrigger_OnPush_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, updates []domain.RefUpdate)) *MirrorTrigger_OnPush_Call {
98+ _c.Run(run)
99+ return _c
100+}
@@ -0,0 +1,100 @@
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+// NewMirrorTrigger creates a new instance of MirrorTrigger. 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 NewMirrorTrigger(t interface {
17+ mock.TestingT
18+ Cleanup(func())
19+}) *MirrorTrigger {
20+ if helper, ok := t.(interface{ Helper() }); ok {
21+ helper.Helper()
22+ }
23+
24+ mock := &MirrorTrigger{}
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+// MirrorTrigger is an autogenerated mock type for the MirrorTrigger type
38+type MirrorTrigger struct {
39+ mock.Mock
40+}
41+
42+type MirrorTrigger_Expecter struct {
43+ mock *mock.Mock
44+}
45+
46+func (_m *MirrorTrigger) EXPECT() *MirrorTrigger_Expecter {
47+ return &MirrorTrigger_Expecter{mock: &_m.Mock}
48+}
49+
50+// OnPush provides a mock function for the type MirrorTrigger
51+func (_mock *MirrorTrigger) OnPush(ctx context.Context, repo domain.RepoName, updates []domain.RefUpdate) {
52+ _mock.Called(ctx, repo, updates)
53+ return
54+}
55+
56+// MirrorTrigger_OnPush_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OnPush'
57+type MirrorTrigger_OnPush_Call struct {
58+ *mock.Call
59+}
60+
61+// OnPush is a helper method to define mock.On call
62+// - ctx context.Context
63+// - repo domain.RepoName
64+// - updates []domain.RefUpdate
65+func (_e *MirrorTrigger_Expecter) OnPush(ctx any, repo any, updates any) *MirrorTrigger_OnPush_Call {
66+ return &MirrorTrigger_OnPush_Call{Call: _e.mock.On("OnPush", ctx, repo, updates)}
67+}
68+
69+func (_c *MirrorTrigger_OnPush_Call) Run(run func(ctx context.Context, repo domain.RepoName, updates []domain.RefUpdate)) *MirrorTrigger_OnPush_Call {
70+ _c.Call.Run(func(args mock.Arguments) {
71+ var arg0 context.Context
72+ if args[0] != nil {
73+ arg0 = args[0].(context.Context)
74+ }
75+ var arg1 domain.RepoName
76+ if args[1] != nil {
77+ arg1 = args[1].(domain.RepoName)
78+ }
79+ var arg2 []domain.RefUpdate
80+ if args[2] != nil {
81+ arg2 = args[2].([]domain.RefUpdate)
82+ }
83+ run(
84+ arg0,
85+ arg1,
86+ arg2,
87+ )
88+ })
89+ return _c
90+}
91+
92+func (_c *MirrorTrigger_OnPush_Call) Return() *MirrorTrigger_OnPush_Call {
93+ _c.Call.Return()
94+ return _c
95+}
96+
97+func (_c *MirrorTrigger_OnPush_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, updates []domain.RefUpdate)) *MirrorTrigger_OnPush_Call {
98+ _c.Run(run)
99+ return _c
100+}
A internal/ports/mocks/MirrorWarner.go +177 -0
@@ -0,0 +1,177 @@
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+// NewMirrorWarner creates a new instance of MirrorWarner. 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 NewMirrorWarner(t interface {
17+ mock.TestingT
18+ Cleanup(func())
19+}) *MirrorWarner {
20+ if helper, ok := t.(interface{ Helper() }); ok {
21+ helper.Helper()
22+ }
23+
24+ mock := &MirrorWarner{}
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+// MirrorWarner is an autogenerated mock type for the MirrorWarner type
38+type MirrorWarner struct {
39+ mock.Mock
40+}
41+
42+type MirrorWarner_Expecter struct {
43+ mock *mock.Mock
44+}
45+
46+func (_m *MirrorWarner) EXPECT() *MirrorWarner_Expecter {
47+ return &MirrorWarner_Expecter{mock: &_m.Mock}
48+}
49+
50+// Badge provides a mock function for the type MirrorWarner
51+func (_mock *MirrorWarner) Badge(ctx context.Context, p domain.Principal, repo domain.RepoName) (domain.MirrorState, string) {
52+ ret := _mock.Called(ctx, p, repo)
53+
54+ if len(ret) == 0 {
55+ panic("no return value specified for Badge")
56+ }
57+
58+ var r0 domain.MirrorState
59+ var r1 string
60+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName) (domain.MirrorState, string)); ok {
61+ return returnFunc(ctx, p, repo)
62+ }
63+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName) domain.MirrorState); ok {
64+ r0 = returnFunc(ctx, p, repo)
65+ } else {
66+ r0 = ret.Get(0).(domain.MirrorState)
67+ }
68+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName) string); ok {
69+ r1 = returnFunc(ctx, p, repo)
70+ } else {
71+ r1 = ret.Get(1).(string)
72+ }
73+ return r0, r1
74+}
75+
76+// MirrorWarner_Badge_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Badge'
77+type MirrorWarner_Badge_Call struct {
78+ *mock.Call
79+}
80+
81+// Badge is a helper method to define mock.On call
82+// - ctx context.Context
83+// - p domain.Principal
84+// - repo domain.RepoName
85+func (_e *MirrorWarner_Expecter) Badge(ctx any, p any, repo any) *MirrorWarner_Badge_Call {
86+ return &MirrorWarner_Badge_Call{Call: _e.mock.On("Badge", ctx, p, repo)}
87+}
88+
89+func (_c *MirrorWarner_Badge_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName)) *MirrorWarner_Badge_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+ var arg2 domain.RepoName
100+ if args[2] != nil {
101+ arg2 = args[2].(domain.RepoName)
102+ }
103+ run(
104+ arg0,
105+ arg1,
106+ arg2,
107+ )
108+ })
109+ return _c
110+}
111+
112+func (_c *MirrorWarner_Badge_Call) Return(mirrorState domain.MirrorState, s string) *MirrorWarner_Badge_Call {
113+ _c.Call.Return(mirrorState, s)
114+ return _c
115+}
116+
117+func (_c *MirrorWarner_Badge_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName) (domain.MirrorState, string)) *MirrorWarner_Badge_Call {
118+ _c.Call.Return(run)
119+ return _c
120+}
121+
122+// Warning provides a mock function for the type MirrorWarner
123+func (_mock *MirrorWarner) Warning(ctx context.Context, repo domain.RepoName) string {
124+ ret := _mock.Called(ctx, repo)
125+
126+ if len(ret) == 0 {
127+ panic("no return value specified for Warning")
128+ }
129+
130+ var r0 string
131+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) string); ok {
132+ r0 = returnFunc(ctx, repo)
133+ } else {
134+ r0 = ret.Get(0).(string)
135+ }
136+ return r0
137+}
138+
139+// MirrorWarner_Warning_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Warning'
140+type MirrorWarner_Warning_Call struct {
141+ *mock.Call
142+}
143+
144+// Warning is a helper method to define mock.On call
145+// - ctx context.Context
146+// - repo domain.RepoName
147+func (_e *MirrorWarner_Expecter) Warning(ctx any, repo any) *MirrorWarner_Warning_Call {
148+ return &MirrorWarner_Warning_Call{Call: _e.mock.On("Warning", ctx, repo)}
149+}
150+
151+func (_c *MirrorWarner_Warning_Call) Run(run func(ctx context.Context, repo domain.RepoName)) *MirrorWarner_Warning_Call {
152+ _c.Call.Run(func(args mock.Arguments) {
153+ var arg0 context.Context
154+ if args[0] != nil {
155+ arg0 = args[0].(context.Context)
156+ }
157+ var arg1 domain.RepoName
158+ if args[1] != nil {
159+ arg1 = args[1].(domain.RepoName)
160+ }
161+ run(
162+ arg0,
163+ arg1,
164+ )
165+ })
166+ return _c
167+}
168+
169+func (_c *MirrorWarner_Warning_Call) Return(s string) *MirrorWarner_Warning_Call {
170+ _c.Call.Return(s)
171+ return _c
172+}
173+
174+func (_c *MirrorWarner_Warning_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName) string) *MirrorWarner_Warning_Call {
175+ _c.Call.Return(run)
176+ return _c
177+}
@@ -0,0 +1,177 @@
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+// NewMirrorWarner creates a new instance of MirrorWarner. 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 NewMirrorWarner(t interface {
17+ mock.TestingT
18+ Cleanup(func())
19+}) *MirrorWarner {
20+ if helper, ok := t.(interface{ Helper() }); ok {
21+ helper.Helper()
22+ }
23+
24+ mock := &MirrorWarner{}
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+// MirrorWarner is an autogenerated mock type for the MirrorWarner type
38+type MirrorWarner struct {
39+ mock.Mock
40+}
41+
42+type MirrorWarner_Expecter struct {
43+ mock *mock.Mock
44+}
45+
46+func (_m *MirrorWarner) EXPECT() *MirrorWarner_Expecter {
47+ return &MirrorWarner_Expecter{mock: &_m.Mock}
48+}
49+
50+// Badge provides a mock function for the type MirrorWarner
51+func (_mock *MirrorWarner) Badge(ctx context.Context, p domain.Principal, repo domain.RepoName) (domain.MirrorState, string) {
52+ ret := _mock.Called(ctx, p, repo)
53+
54+ if len(ret) == 0 {
55+ panic("no return value specified for Badge")
56+ }
57+
58+ var r0 domain.MirrorState
59+ var r1 string
60+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName) (domain.MirrorState, string)); ok {
61+ return returnFunc(ctx, p, repo)
62+ }
63+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.Principal, domain.RepoName) domain.MirrorState); ok {
64+ r0 = returnFunc(ctx, p, repo)
65+ } else {
66+ r0 = ret.Get(0).(domain.MirrorState)
67+ }
68+ if returnFunc, ok := ret.Get(1).(func(context.Context, domain.Principal, domain.RepoName) string); ok {
69+ r1 = returnFunc(ctx, p, repo)
70+ } else {
71+ r1 = ret.Get(1).(string)
72+ }
73+ return r0, r1
74+}
75+
76+// MirrorWarner_Badge_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Badge'
77+type MirrorWarner_Badge_Call struct {
78+ *mock.Call
79+}
80+
81+// Badge is a helper method to define mock.On call
82+// - ctx context.Context
83+// - p domain.Principal
84+// - repo domain.RepoName
85+func (_e *MirrorWarner_Expecter) Badge(ctx any, p any, repo any) *MirrorWarner_Badge_Call {
86+ return &MirrorWarner_Badge_Call{Call: _e.mock.On("Badge", ctx, p, repo)}
87+}
88+
89+func (_c *MirrorWarner_Badge_Call) Run(run func(ctx context.Context, p domain.Principal, repo domain.RepoName)) *MirrorWarner_Badge_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+ var arg2 domain.RepoName
100+ if args[2] != nil {
101+ arg2 = args[2].(domain.RepoName)
102+ }
103+ run(
104+ arg0,
105+ arg1,
106+ arg2,
107+ )
108+ })
109+ return _c
110+}
111+
112+func (_c *MirrorWarner_Badge_Call) Return(mirrorState domain.MirrorState, s string) *MirrorWarner_Badge_Call {
113+ _c.Call.Return(mirrorState, s)
114+ return _c
115+}
116+
117+func (_c *MirrorWarner_Badge_Call) RunAndReturn(run func(ctx context.Context, p domain.Principal, repo domain.RepoName) (domain.MirrorState, string)) *MirrorWarner_Badge_Call {
118+ _c.Call.Return(run)
119+ return _c
120+}
121+
122+// Warning provides a mock function for the type MirrorWarner
123+func (_mock *MirrorWarner) Warning(ctx context.Context, repo domain.RepoName) string {
124+ ret := _mock.Called(ctx, repo)
125+
126+ if len(ret) == 0 {
127+ panic("no return value specified for Warning")
128+ }
129+
130+ var r0 string
131+ if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) string); ok {
132+ r0 = returnFunc(ctx, repo)
133+ } else {
134+ r0 = ret.Get(0).(string)
135+ }
136+ return r0
137+}
138+
139+// MirrorWarner_Warning_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Warning'
140+type MirrorWarner_Warning_Call struct {
141+ *mock.Call
142+}
143+
144+// Warning is a helper method to define mock.On call
145+// - ctx context.Context
146+// - repo domain.RepoName
147+func (_e *MirrorWarner_Expecter) Warning(ctx any, repo any) *MirrorWarner_Warning_Call {
148+ return &MirrorWarner_Warning_Call{Call: _e.mock.On("Warning", ctx, repo)}
149+}
150+
151+func (_c *MirrorWarner_Warning_Call) Run(run func(ctx context.Context, repo domain.RepoName)) *MirrorWarner_Warning_Call {
152+ _c.Call.Run(func(args mock.Arguments) {
153+ var arg0 context.Context
154+ if args[0] != nil {
155+ arg0 = args[0].(context.Context)
156+ }
157+ var arg1 domain.RepoName
158+ if args[1] != nil {
159+ arg1 = args[1].(domain.RepoName)
160+ }
161+ run(
162+ arg0,
163+ arg1,
164+ )
165+ })
166+ return _c
167+}
168+
169+func (_c *MirrorWarner_Warning_Call) Return(s string) *MirrorWarner_Warning_Call {
170+ _c.Call.Return(s)
171+ return _c
172+}
173+
174+func (_c *MirrorWarner_Warning_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName) string) *MirrorWarner_Warning_Call {
175+ _c.Call.Return(run)
176+ return _c
177+}

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 42

↑↓ moveenter openesc close