feat(domain): add merge requests, repository settings and the signing key types
MRTitle and PlainText follow the input rules of ADR 0017 (valid UTF-8, no control characters, no bidi override or isolate, length limits); a request that breaks rules is rejected whole with the fields named. The squash commit is built in the domain (author of the first commit, Co-authored-by trailers, gpgsig header). Ports for the merge request store, the merge Git operations and the commit signer are declared with their mocks.
- commit
- b60fb7665a9ef9872ea87342060b512f9353d6c0
- author
- brice · · 2026-10-10 16:49 UTC
- parent
- fef88c6
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
14 files · +2562 -1
M .mockery.yml +3 -0
| @@ -15,3 +15,6 @@ packages: | |||
| 15 | 15 | GitBackend: | |
| 16 | 16 | EventBus: | |
| 17 | 17 | RepoReader: | |
| 18 | + | MergeRequestStore: | |
| 19 | + | MergeGit: | |
| 20 | + | CommitSigner: | |
| @@ -15,3 +15,6 @@ packages: | |||||
| 15 | GitBackend: | 15 | GitBackend: | ||
| 16 | EventBus: | 16 | EventBus: | ||
| 17 | RepoReader: | 17 | RepoReader: | ||
| 18 | + | MergeRequestStore: | |||
| 19 | + | MergeGit: | |||
| 20 | + | CommitSigner: | |||
M internal/domain/event.go +50 -0
| @@ -111,6 +111,56 @@ type PushEvent struct { | |||
| 111 | 111 | // EventKind implements Event. | |
| 112 | 112 | func (PushEvent) EventKind() string { return "repo.pushed" } | |
| 113 | 113 | ||
| 114 | + | // MergeRequestOpened is published when a merge request is opened. | |
| 115 | + | type MergeRequestOpened struct { | |
| 116 | + | MergeRequest MergeRequest | |
| 117 | + | At time.Time | |
| 118 | + | // Visibility and CreatedAt are those of the repository when the event | |
| 119 | + | // was published, like PushEvent's. | |
| 120 | + | Visibility Visibility | |
| 121 | + | CreatedAt time.Time | |
| 122 | + | } | |
| 123 | + | ||
| 124 | + | // EventKind implements Event. | |
| 125 | + | func (MergeRequestOpened) EventKind() string { return "mr.opened" } | |
| 126 | + | ||
| 127 | + | // MergeRequestMerged is published when a merge request was merged, before the | |
| 128 | + | // PushEvent of the target branch. | |
| 129 | + | type MergeRequestMerged struct { | |
| 130 | + | MergeRequest MergeRequest | |
| 131 | + | At time.Time | |
| 132 | + | Visibility Visibility | |
| 133 | + | CreatedAt time.Time | |
| 134 | + | } | |
| 135 | + | ||
| 136 | + | // EventKind implements Event. | |
| 137 | + | func (MergeRequestMerged) EventKind() string { return "mr.merged" } | |
| 138 | + | ||
| 139 | + | // MergeRequestClosed is published when a merge request was closed without | |
| 140 | + | // merging. | |
| 141 | + | type MergeRequestClosed struct { | |
| 142 | + | MergeRequest MergeRequest | |
| 143 | + | At time.Time | |
| 144 | + | Visibility Visibility | |
| 145 | + | CreatedAt time.Time | |
| 146 | + | } | |
| 147 | + | ||
| 148 | + | // EventKind implements Event. | |
| 149 | + | func (MergeRequestClosed) EventKind() string { return "mr.closed" } | |
| 150 | + | ||
| 151 | + | // CommentAdded is published when a comment was added to a merge request. | |
| 152 | + | type CommentAdded struct { | |
| 153 | + | Repo RepoName | |
| 154 | + | Number int64 | |
| 155 | + | Comment Comment | |
| 156 | + | At time.Time | |
| 157 | + | Visibility Visibility | |
| 158 | + | CreatedAt time.Time | |
| 159 | + | } | |
| 160 | + | ||
| 161 | + | // EventKind implements Event. | |
| 162 | + | func (CommentAdded) EventKind() string { return "mr.comment_added" } | |
| 163 | + | ||
| 114 | 164 | // EventGap tells a subscriber that it missed events because it was too slow; | |
| 115 | 165 | // it must refresh its whole view. | |
| 116 | 166 | type EventGap struct{} | |
| @@ -111,6 +111,56 @@ type PushEvent struct { | |||||
| 111 | // EventKind implements Event. | 111 | // EventKind implements Event. | ||
| 112 | func (PushEvent) EventKind() string { return "repo.pushed" } | 112 | func (PushEvent) EventKind() string { return "repo.pushed" } | ||
| 113 | 113 | ||||
| 114 | + | // MergeRequestOpened is published when a merge request is opened. | |||
| 115 | + | type MergeRequestOpened struct { | |||
| 116 | + | MergeRequest MergeRequest | |||
| 117 | + | At time.Time | |||
| 118 | + | // Visibility and CreatedAt are those of the repository when the event | |||
| 119 | + | // was published, like PushEvent's. | |||
| 120 | + | Visibility Visibility | |||
| 121 | + | CreatedAt time.Time | |||
| 122 | + | } | |||
| 123 | + | ||||
| 124 | + | // EventKind implements Event. | |||
| 125 | + | func (MergeRequestOpened) EventKind() string { return "mr.opened" } | |||
| 126 | + | ||||
| 127 | + | // MergeRequestMerged is published when a merge request was merged, before the | |||
| 128 | + | // PushEvent of the target branch. | |||
| 129 | + | type MergeRequestMerged struct { | |||
| 130 | + | MergeRequest MergeRequest | |||
| 131 | + | At time.Time | |||
| 132 | + | Visibility Visibility | |||
| 133 | + | CreatedAt time.Time | |||
| 134 | + | } | |||
| 135 | + | ||||
| 136 | + | // EventKind implements Event. | |||
| 137 | + | func (MergeRequestMerged) EventKind() string { return "mr.merged" } | |||
| 138 | + | ||||
| 139 | + | // MergeRequestClosed is published when a merge request was closed without | |||
| 140 | + | // merging. | |||
| 141 | + | type MergeRequestClosed struct { | |||
| 142 | + | MergeRequest MergeRequest | |||
| 143 | + | At time.Time | |||
| 144 | + | Visibility Visibility | |||
| 145 | + | CreatedAt time.Time | |||
| 146 | + | } | |||
| 147 | + | ||||
| 148 | + | // EventKind implements Event. | |||
| 149 | + | func (MergeRequestClosed) EventKind() string { return "mr.closed" } | |||
| 150 | + | ||||
| 151 | + | // CommentAdded is published when a comment was added to a merge request. | |||
| 152 | + | type CommentAdded struct { | |||
| 153 | + | Repo RepoName | |||
| 154 | + | Number int64 | |||
| 155 | + | Comment Comment | |||
| 156 | + | At time.Time | |||
| 157 | + | Visibility Visibility | |||
| 158 | + | CreatedAt time.Time | |||
| 159 | + | } | |||
| 160 | + | ||||
| 161 | + | // EventKind implements Event. | |||
| 162 | + | func (CommentAdded) EventKind() string { return "mr.comment_added" } | |||
| 163 | + | ||||
| 114 | // EventGap tells a subscriber that it missed events because it was too slow; | 164 | // EventGap tells a subscriber that it missed events because it was too slow; | ||
| 115 | // it must refresh its whole view. | 165 | // it must refresh its whole view. | ||
| 116 | type EventGap struct{} | 166 | type EventGap struct{} | ||
M internal/domain/git.go +16 -0
| @@ -472,3 +472,19 @@ type DiffLimits struct { | |||
| 472 | 472 | func DefaultDiffLimits() DiffLimits { | |
| 473 | 473 | return DiffLimits{MaxFiles: 300, MaxFileLines: 2000, MaxTotalLines: 20000, MaxTotalBytes: 8 << 20} | |
| 474 | 474 | } | |
| 475 | + | ||
| 476 | + | // ParseBranchName validates s as the short name of a branch ("feat/x"), with | |
| 477 | + | // the syntax rules of ParseRevision, and rejects a full ref name, which would | |
| 478 | + | // not be short. | |
| 479 | + | func ParseBranchName(s string) (string, error) { | |
| 480 | + | switch { | |
| 481 | + | case s == "": | |
| 482 | + | return "", fmt.Errorf("%w: empty branch name", ErrInvalidRevision) | |
| 483 | + | case strings.HasPrefix(s, "refs/"): | |
| 484 | + | return "", fmt.Errorf("%w: %q: give the short name of the branch", ErrInvalidRevision, s) | |
| 485 | + | } | |
| 486 | + | if reason := refNameProblem(s); reason != "" { | |
| 487 | + | return "", fmt.Errorf("%w: %q: %s", ErrInvalidRevision, s, reason) | |
| 488 | + | } | |
| 489 | + | return s, nil | |
| 490 | + | } | |
| @@ -472,3 +472,19 @@ type DiffLimits struct { | |||||
| 472 | func DefaultDiffLimits() DiffLimits { | 472 | func DefaultDiffLimits() DiffLimits { | ||
| 473 | return DiffLimits{MaxFiles: 300, MaxFileLines: 2000, MaxTotalLines: 20000, MaxTotalBytes: 8 << 20} | 473 | return DiffLimits{MaxFiles: 300, MaxFileLines: 2000, MaxTotalLines: 20000, MaxTotalBytes: 8 << 20} | ||
| 474 | } | 474 | } | ||
| 475 | + | ||||
| 476 | + | // ParseBranchName validates s as the short name of a branch ("feat/x"), with | |||
| 477 | + | // the syntax rules of ParseRevision, and rejects a full ref name, which would | |||
| 478 | + | // not be short. | |||
| 479 | + | func ParseBranchName(s string) (string, error) { | |||
| 480 | + | switch { | |||
| 481 | + | case s == "": | |||
| 482 | + | return "", fmt.Errorf("%w: empty branch name", ErrInvalidRevision) | |||
| 483 | + | case strings.HasPrefix(s, "refs/"): | |||
| 484 | + | return "", fmt.Errorf("%w: %q: give the short name of the branch", ErrInvalidRevision, s) | |||
| 485 | + | } | |||
| 486 | + | if reason := refNameProblem(s); reason != "" { | |||
| 487 | + | return "", fmt.Errorf("%w: %q: %s", ErrInvalidRevision, s, reason) | |||
| 488 | + | } | |||
| 489 | + | return s, nil | |||
| 490 | + | } | |||
A internal/domain/mergerequest.go +306 -0
| @@ -0,0 +1,306 @@ | |||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | ||
| 3 | + | package domain | |
| 4 | + | ||
| 5 | + | import ( | |
| 6 | + | "fmt" | |
| 7 | + | "slices" | |
| 8 | + | "strings" | |
| 9 | + | "time" | |
| 10 | + | ) | |
| 11 | + | ||
| 12 | + | // MergeMode is how a merge request is merged (ADR 0017). | |
| 13 | + | type MergeMode string | |
| 14 | + | ||
| 15 | + | // Merge modes. | |
| 16 | + | const ( | |
| 17 | + | // MergeModeFastForward moves the target to the head; no commit is made. | |
| 18 | + | MergeModeFastForward MergeMode = "fast-forward" | |
| 19 | + | // MergeModeSquash makes one new signed commit with the tree of the head. | |
| 20 | + | MergeModeSquash MergeMode = "squash" | |
| 21 | + | ) | |
| 22 | + | ||
| 23 | + | // MergeModes returns the two modes in their canonical order. | |
| 24 | + | func MergeModes() []MergeMode { return []MergeMode{MergeModeFastForward, MergeModeSquash} } | |
| 25 | + | ||
| 26 | + | // ParseMergeMode parses "fast-forward" or "squash". | |
| 27 | + | func ParseMergeMode(s string) (MergeMode, error) { | |
| 28 | + | m := MergeMode(s) | |
| 29 | + | if slices.Contains(MergeModes(), m) { | |
| 30 | + | return m, nil | |
| 31 | + | } | |
| 32 | + | return "", fmt.Errorf("%w: merge mode %q (want fast-forward or squash)", ErrInvalid, s) | |
| 33 | + | } | |
| 34 | + | ||
| 35 | + | // String returns the mode as text. | |
| 36 | + | func (m MergeMode) String() string { return string(m) } | |
| 37 | + | ||
| 38 | + | // RepoSettings are the settings of a repository that govern its merge | |
| 39 | + | // requests. | |
| 40 | + | type RepoSettings struct { | |
| 41 | + | // AllowedMergeModes is never empty, in canonical order, without | |
| 42 | + | // duplicates. | |
| 43 | + | AllowedMergeModes []MergeMode | |
| 44 | + | // FailingChecksBlock says that a failing check prevents the merge. | |
| 45 | + | FailingChecksBlock bool | |
| 46 | + | } | |
| 47 | + | ||
| 48 | + | // DefaultRepoSettings are the settings of a new repository: both modes, and | |
| 49 | + | // failing checks block the merge. | |
| 50 | + | func DefaultRepoSettings() RepoSettings { | |
| 51 | + | return RepoSettings{AllowedMergeModes: MergeModes(), FailingChecksBlock: true} | |
| 52 | + | } | |
| 53 | + | ||
| 54 | + | // IsZero reports whether s holds no mode, which only the zero value does. | |
| 55 | + | func (s RepoSettings) IsZero() bool { return len(s.AllowedMergeModes) == 0 } | |
| 56 | + | ||
| 57 | + | // Allows reports whether the mode may be used. | |
| 58 | + | func (s RepoSettings) Allows(m MergeMode) bool { return slices.Contains(s.AllowedMergeModes, m) } | |
| 59 | + | ||
| 60 | + | // NormalizeMergeModes validates a list of modes for the settings: it must not | |
| 61 | + | // be empty or hold an unknown mode. The result is in canonical order, without | |
| 62 | + | // duplicates. | |
| 63 | + | func NormalizeMergeModes(modes []MergeMode) ([]MergeMode, error) { | |
| 64 | + | if len(modes) == 0 { | |
| 65 | + | return nil, fmt.Errorf("%w: at least one merge mode is required", ErrInvalid) | |
| 66 | + | } | |
| 67 | + | for _, m := range modes { | |
| 68 | + | if _, err := ParseMergeMode(m.String()); err != nil { | |
| 69 | + | return nil, err | |
| 70 | + | } | |
| 71 | + | } | |
| 72 | + | var out []MergeMode | |
| 73 | + | for _, m := range MergeModes() { | |
| 74 | + | if slices.Contains(modes, m) { | |
| 75 | + | out = append(out, m) | |
| 76 | + | } | |
| 77 | + | } | |
| 78 | + | return out, nil | |
| 79 | + | } | |
| 80 | + | ||
| 81 | + | // MRState is where a merge request is in its life. | |
| 82 | + | type MRState string | |
| 83 | + | ||
| 84 | + | // Merge request states. | |
| 85 | + | const ( | |
| 86 | + | // MRStateOpen awaits review. | |
| 87 | + | MRStateOpen MRState = "open" | |
| 88 | + | // MRStateMerged is final: the branch was merged. | |
| 89 | + | MRStateMerged MRState = "merged" | |
| 90 | + | // MRStateClosed is final: the request was closed without merging. | |
| 91 | + | MRStateClosed MRState = "closed" | |
| 92 | + | ) | |
| 93 | + | ||
| 94 | + | // DiffSide is the side of a diff a comment is attached to. | |
| 95 | + | type DiffSide string | |
| 96 | + | ||
| 97 | + | // Diff sides. The empty side marks a general comment. | |
| 98 | + | const ( | |
| 99 | + | // SideNone is the side of a general comment. | |
| 100 | + | SideNone DiffSide = "" | |
| 101 | + | // SideOld counts lines of the old file. | |
| 102 | + | SideOld DiffSide = "old" | |
| 103 | + | // SideNew counts lines of the new file. | |
| 104 | + | SideNew DiffSide = "new" | |
| 105 | + | ) | |
| 106 | + | ||
| 107 | + | // MergeRequest asks to merge one branch into another of the same repository. | |
| 108 | + | type MergeRequest struct { | |
| 109 | + | Repo RepoName | |
| 110 | + | Number int64 | |
| 111 | + | Title MRTitle | |
| 112 | + | // Description may be empty. | |
| 113 | + | Description PlainText | |
| 114 | + | // Source and Target are short branch names. | |
| 115 | + | Source, Target string | |
| 116 | + | State MRState | |
| 117 | + | // Author is the label of the token that opened it, "root" or "owner". | |
| 118 | + | Author string | |
| 119 | + | CreatedAt time.Time | |
| 120 | + | UpdatedAt time.Time | |
| 121 | + | // HeadID is the head of the source branch: stored when the request is | |
| 122 | + | // merged or closed, and filled from Git at every read while it is open | |
| 123 | + | // (zero when the branch is gone). | |
| 124 | + | HeadID ObjectID | |
| 125 | + | // BaseID is the merge base of the target and the head when the request | |
| 126 | + | // was merged or closed; zero while it is open. | |
| 127 | + | BaseID ObjectID | |
| 128 | + | // MergedCommitID is the tip of the target after the merge; zero unless | |
| 129 | + | // merged. | |
| 130 | + | MergedCommitID ObjectID | |
| 131 | + | // MergeMode is the mode used; empty unless merged. | |
| 132 | + | MergeMode MergeMode | |
| 133 | + | // CommentSeq is the id of the last comment of the request. | |
| 134 | + | CommentSeq int64 | |
| 135 | + | // Version is set by the store on read: Update fails with ErrConflict when | |
| 136 | + | // the stored request changed since. Opaque to everyone else. | |
| 137 | + | Version uint64 | |
| 138 | + | } | |
| 139 | + | ||
| 140 | + | // IsOpen reports whether the request awaits review. | |
| 141 | + | func (m MergeRequest) IsOpen() bool { return m.State == MRStateOpen } | |
| 142 | + | ||
| 143 | + | // Comment is a note on a merge request, general or on a line of its diff. | |
| 144 | + | type Comment struct { | |
| 145 | + | // ID is unique within the merge request, in order of creation. | |
| 146 | + | ID int64 | |
| 147 | + | Author string | |
| 148 | + | // Path, Line and Side are empty for a general comment. | |
| 149 | + | Path string | |
| 150 | + | Line int | |
| 151 | + | Side DiffSide | |
| 152 | + | // CommitID is the head of the request when the comment was written; zero | |
| 153 | + | // when the source branch was gone. | |
| 154 | + | CommitID ObjectID | |
| 155 | + | Text PlainText | |
| 156 | + | Resolved bool | |
| 157 | + | CreatedAt time.Time | |
| 158 | + | // Version is set by the store on read, like MergeRequest.Version. | |
| 159 | + | Version uint64 | |
| 160 | + | } | |
| 161 | + | ||
| 162 | + | // IsGeneral reports whether the comment is not attached to a line. | |
| 163 | + | func (c Comment) IsGeneral() bool { return c.Path == "" } | |
| 164 | + | ||
| 165 | + | // HasLine reports whether the diff has a line numbered line on side in the | |
| 166 | + | // file path: the new path of a file, or the old path of a deleted one. The new | |
| 167 | + | // side holds the context and addition lines, the old side the context and | |
| 168 | + | // deletion lines. A file the diff truncated has no lines. | |
| 169 | + | func (d Diff) HasLine(path string, side DiffSide, line int) bool { | |
| 170 | + | if path == "" || line < 1 { | |
| 171 | + | return false | |
| 172 | + | } | |
| 173 | + | for _, f := range d.Files { | |
| 174 | + | name := f.NewPath | |
| 175 | + | if f.Status == FileDeleted { | |
| 176 | + | name = f.OldPath | |
| 177 | + | } | |
| 178 | + | if name != path { | |
| 179 | + | continue | |
| 180 | + | } | |
| 181 | + | for _, h := range f.Hunks { | |
| 182 | + | for _, l := range h.Lines { | |
| 183 | + | switch { | |
| 184 | + | case side == SideNew && l.Kind != LineDeletion && l.NewNumber == line: | |
| 185 | + | return true | |
| 186 | + | case side == SideOld && l.Kind != LineAddition && l.OldNumber == line: | |
| 187 | + | return true | |
| 188 | + | } | |
| 189 | + | } | |
| 190 | + | } | |
| 191 | + | } | |
| 192 | + | return false | |
| 193 | + | } | |
| 194 | + | ||
| 195 | + | // Identity is a name and an email address, such as the committer of the | |
| 196 | + | // commits ermite creates. | |
| 197 | + | type Identity struct { | |
| 198 | + | Name, Email string | |
| 199 | + | } | |
| 200 | + | ||
| 201 | + | // SquashCommit is the commit a squash merge creates: the tree of the head on | |
| 202 | + | // top of the target. | |
| 203 | + | type SquashCommit struct { | |
| 204 | + | Tree, Parent ObjectID | |
| 205 | + | Author, Committer Signature | |
| 206 | + | Message string | |
| 207 | + | } | |
| 208 | + | ||
| 209 | + | // MaxSquashCommits bounds the commits a squash lists in its message. | |
| 210 | + | const MaxSquashCommits = 1000 | |
| 211 | + | ||
| 212 | + | // NewSquashCommit builds the squash commit of a merge request (ADR 0022). | |
| 213 | + | // commits are the commits of the branch that the target lacks, newest first, | |
| 214 | + | // as Git lists them, and not empty. The author is the author of the oldest | |
| 215 | + | // one, with its date; the other distinct authors become Co-authored-by | |
| 216 | + | // trailers, in order of appearance. The message is the title, " (!<number>)", | |
| 217 | + | // a blank line, the summaries of the commits oldest first as a list, and the | |
| 218 | + | // trailers. | |
| 219 | + | func NewSquashCommit(mr MergeRequest, tree, parent ObjectID, commits []Commit, committer Identity, at time.Time) SquashCommit { | |
| 220 | + | oldest := slices.Clone(commits) | |
| 221 | + | slices.Reverse(oldest) | |
| 222 | + | var first Signature | |
| 223 | + | if len(oldest) > 0 { | |
| 224 | + | first = oldest[0].Author | |
| 225 | + | } | |
| 226 | + | ||
| 227 | + | var msg strings.Builder | |
| 228 | + | fmt.Fprintf(&msg, "%s (!%d)\n", mr.Title.String(), mr.Number) | |
| 229 | + | if len(oldest) > 0 { | |
| 230 | + | msg.WriteString("\n") | |
| 231 | + | } | |
| 232 | + | var trailers []string | |
| 233 | + | seen := map[string]bool{strings.ToLower(first.Email): true} | |
| 234 | + | for _, c := range oldest { | |
| 235 | + | msg.WriteString("- " + singleLine(c.Summary) + "\n") | |
| 236 | + | key := strings.ToLower(c.Author.Email) | |
| 237 | + | if !seen[key] { | |
| 238 | + | seen[key] = true | |
| 239 | + | trailers = append(trailers, "Co-authored-by: "+identLine(c.Author.Name, c.Author.Email)) | |
| 240 | + | } | |
| 241 | + | } | |
| 242 | + | if len(trailers) > 0 { | |
| 243 | + | msg.WriteString("\n" + strings.Join(trailers, "\n") + "\n") | |
| 244 | + | } | |
| 245 | + | return SquashCommit{ | |
| 246 | + | Tree: tree, Parent: parent, | |
| 247 | + | Author: first, | |
| 248 | + | Committer: Signature{Name: committer.Name, Email: committer.Email, Time: at.UTC()}, | |
| 249 | + | Message: msg.String(), | |
| 250 | + | } | |
| 251 | + | } | |
| 252 | + | ||
| 253 | + | // Payload returns the text of the commit object without a signature, which is | |
| 254 | + | // what the signature covers. | |
| 255 | + | func (c SquashCommit) Payload() []byte { return c.encode("") } | |
| 256 | + | ||
| 257 | + | // Signed returns the text of the commit object with the armored SSH signature | |
| 258 | + | // in its gpgsig header. | |
| 259 | + | func (c SquashCommit) Signed(armored string) []byte { return c.encode(armored) } | |
| 260 | + | ||
| 261 | + | func (c SquashCommit) encode(armored string) []byte { | |
| 262 | + | var b strings.Builder | |
| 263 | + | b.WriteString("tree " + c.Tree.String() + "\n") | |
| 264 | + | b.WriteString("parent " + c.Parent.String() + "\n") | |
| 265 | + | b.WriteString("author " + signatureLine(c.Author) + "\n") | |
| 266 | + | b.WriteString("committer " + signatureLine(c.Committer) + "\n") | |
| 267 | + | if armored != "" { | |
| 268 | + | lines := strings.Split(strings.TrimRight(armored, "\n"), "\n") | |
| 269 | + | b.WriteString("gpgsig " + lines[0] + "\n") | |
| 270 | + | for _, l := range lines[1:] { | |
| 271 | + | b.WriteString(" " + l + "\n") | |
| 272 | + | } | |
| 273 | + | } | |
| 274 | + | b.WriteString("\n") | |
| 275 | + | b.WriteString(c.Message) | |
| 276 | + | return []byte(b.String()) | |
| 277 | + | } | |
| 278 | + | ||
| 279 | + | func signatureLine(s Signature) string { | |
| 280 | + | return fmt.Sprintf("%s %d +0000", identLine(s.Name, s.Email), s.Time.Unix()) | |
| 281 | + | } | |
| 282 | + | ||
| 283 | + | // identLine formats "Name <email>", dropping the characters Git does not | |
| 284 | + | // allow in an identity. | |
| 285 | + | func identLine(name, email string) string { | |
| 286 | + | clean := func(s string) string { | |
| 287 | + | return strings.TrimSpace(strings.Map(func(r rune) rune { | |
| 288 | + | if r == '<' || r == '>' || r < 0x20 || r == 0x7f { | |
| 289 | + | return -1 | |
| 290 | + | } | |
| 291 | + | return r | |
| 292 | + | }, s)) | |
| 293 | + | } | |
| 294 | + | return clean(name) + " <" + clean(email) + ">" | |
| 295 | + | } | |
| 296 | + | ||
| 297 | + | // singleLine replaces the control characters of a commit summary, so that it | |
| 298 | + | // stays one line of the message. | |
| 299 | + | func singleLine(s string) string { | |
| 300 | + | return strings.Map(func(r rune) rune { | |
| 301 | + | if r < 0x20 || r == 0x7f { | |
| 302 | + | return ' ' | |
| 303 | + | } | |
| 304 | + | return r | |
| 305 | + | }, s) | |
| 306 | + | } | |
| @@ -0,0 +1,306 @@ | |||||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |||
| 2 | + | ||||
| 3 | + | package domain | |||
| 4 | + | ||||
| 5 | + | import ( | |||
| 6 | + | "fmt" | |||
| 7 | + | "slices" | |||
| 8 | + | "strings" | |||
| 9 | + | "time" | |||
| 10 | + | ) | |||
| 11 | + | ||||
| 12 | + | // MergeMode is how a merge request is merged (ADR 0017). | |||
| 13 | + | type MergeMode string | |||
| 14 | + | ||||
| 15 | + | // Merge modes. | |||
| 16 | + | const ( | |||
| 17 | + | // MergeModeFastForward moves the target to the head; no commit is made. | |||
| 18 | + | MergeModeFastForward MergeMode = "fast-forward" | |||
| 19 | + | // MergeModeSquash makes one new signed commit with the tree of the head. | |||
| 20 | + | MergeModeSquash MergeMode = "squash" | |||
| 21 | + | ) | |||
| 22 | + | ||||
| 23 | + | // MergeModes returns the two modes in their canonical order. | |||
| 24 | + | func MergeModes() []MergeMode { return []MergeMode{MergeModeFastForward, MergeModeSquash} } | |||
| 25 | + | ||||
| 26 | + | // ParseMergeMode parses "fast-forward" or "squash". | |||
| 27 | + | func ParseMergeMode(s string) (MergeMode, error) { | |||
| 28 | + | m := MergeMode(s) | |||
| 29 | + | if slices.Contains(MergeModes(), m) { | |||
| 30 | + | return m, nil | |||
| 31 | + | } | |||
| 32 | + | return "", fmt.Errorf("%w: merge mode %q (want fast-forward or squash)", ErrInvalid, s) | |||
| 33 | + | } | |||
| 34 | + | ||||
| 35 | + | // String returns the mode as text. | |||
| 36 | + | func (m MergeMode) String() string { return string(m) } | |||
| 37 | + | ||||
| 38 | + | // RepoSettings are the settings of a repository that govern its merge | |||
| 39 | + | // requests. | |||
| 40 | + | type RepoSettings struct { | |||
| 41 | + | // AllowedMergeModes is never empty, in canonical order, without | |||
| 42 | + | // duplicates. | |||
| 43 | + | AllowedMergeModes []MergeMode | |||
| 44 | + | // FailingChecksBlock says that a failing check prevents the merge. | |||
| 45 | + | FailingChecksBlock bool | |||
| 46 | + | } | |||
| 47 | + | ||||
| 48 | + | // DefaultRepoSettings are the settings of a new repository: both modes, and | |||
| 49 | + | // failing checks block the merge. | |||
| 50 | + | func DefaultRepoSettings() RepoSettings { | |||
| 51 | + | return RepoSettings{AllowedMergeModes: MergeModes(), FailingChecksBlock: true} | |||
| 52 | + | } | |||
| 53 | + | ||||
| 54 | + | // IsZero reports whether s holds no mode, which only the zero value does. | |||
| 55 | + | func (s RepoSettings) IsZero() bool { return len(s.AllowedMergeModes) == 0 } | |||
| 56 | + | ||||
| 57 | + | // Allows reports whether the mode may be used. | |||
| 58 | + | func (s RepoSettings) Allows(m MergeMode) bool { return slices.Contains(s.AllowedMergeModes, m) } | |||
| 59 | + | ||||
| 60 | + | // NormalizeMergeModes validates a list of modes for the settings: it must not | |||
| 61 | + | // be empty or hold an unknown mode. The result is in canonical order, without | |||
| 62 | + | // duplicates. | |||
| 63 | + | func NormalizeMergeModes(modes []MergeMode) ([]MergeMode, error) { | |||
| 64 | + | if len(modes) == 0 { | |||
| 65 | + | return nil, fmt.Errorf("%w: at least one merge mode is required", ErrInvalid) | |||
| 66 | + | } | |||
| 67 | + | for _, m := range modes { | |||
| 68 | + | if _, err := ParseMergeMode(m.String()); err != nil { | |||
| 69 | + | return nil, err | |||
| 70 | + | } | |||
| 71 | + | } | |||
| 72 | + | var out []MergeMode | |||
| 73 | + | for _, m := range MergeModes() { | |||
| 74 | + | if slices.Contains(modes, m) { | |||
| 75 | + | out = append(out, m) | |||
| 76 | + | } | |||
| 77 | + | } | |||
| 78 | + | return out, nil | |||
| 79 | + | } | |||
| 80 | + | ||||
| 81 | + | // MRState is where a merge request is in its life. | |||
| 82 | + | type MRState string | |||
| 83 | + | ||||
| 84 | + | // Merge request states. | |||
| 85 | + | const ( | |||
| 86 | + | // MRStateOpen awaits review. | |||
| 87 | + | MRStateOpen MRState = "open" | |||
| 88 | + | // MRStateMerged is final: the branch was merged. | |||
| 89 | + | MRStateMerged MRState = "merged" | |||
| 90 | + | // MRStateClosed is final: the request was closed without merging. | |||
| 91 | + | MRStateClosed MRState = "closed" | |||
| 92 | + | ) | |||
| 93 | + | ||||
| 94 | + | // DiffSide is the side of a diff a comment is attached to. | |||
| 95 | + | type DiffSide string | |||
| 96 | + | ||||
| 97 | + | // Diff sides. The empty side marks a general comment. | |||
| 98 | + | const ( | |||
| 99 | + | // SideNone is the side of a general comment. | |||
| 100 | + | SideNone DiffSide = "" | |||
| 101 | + | // SideOld counts lines of the old file. | |||
| 102 | + | SideOld DiffSide = "old" | |||
| 103 | + | // SideNew counts lines of the new file. | |||
| 104 | + | SideNew DiffSide = "new" | |||
| 105 | + | ) | |||
| 106 | + | ||||
| 107 | + | // MergeRequest asks to merge one branch into another of the same repository. | |||
| 108 | + | type MergeRequest struct { | |||
| 109 | + | Repo RepoName | |||
| 110 | + | Number int64 | |||
| 111 | + | Title MRTitle | |||
| 112 | + | // Description may be empty. | |||
| 113 | + | Description PlainText | |||
| 114 | + | // Source and Target are short branch names. | |||
| 115 | + | Source, Target string | |||
| 116 | + | State MRState | |||
| 117 | + | // Author is the label of the token that opened it, "root" or "owner". | |||
| 118 | + | Author string | |||
| 119 | + | CreatedAt time.Time | |||
| 120 | + | UpdatedAt time.Time | |||
| 121 | + | // HeadID is the head of the source branch: stored when the request is | |||
| 122 | + | // merged or closed, and filled from Git at every read while it is open | |||
| 123 | + | // (zero when the branch is gone). | |||
| 124 | + | HeadID ObjectID | |||
| 125 | + | // BaseID is the merge base of the target and the head when the request | |||
| 126 | + | // was merged or closed; zero while it is open. | |||
| 127 | + | BaseID ObjectID | |||
| 128 | + | // MergedCommitID is the tip of the target after the merge; zero unless | |||
| 129 | + | // merged. | |||
| 130 | + | MergedCommitID ObjectID | |||
| 131 | + | // MergeMode is the mode used; empty unless merged. | |||
| 132 | + | MergeMode MergeMode | |||
| 133 | + | // CommentSeq is the id of the last comment of the request. | |||
| 134 | + | CommentSeq int64 | |||
| 135 | + | // Version is set by the store on read: Update fails with ErrConflict when | |||
| 136 | + | // the stored request changed since. Opaque to everyone else. | |||
| 137 | + | Version uint64 | |||
| 138 | + | } | |||
| 139 | + | ||||
| 140 | + | // IsOpen reports whether the request awaits review. | |||
| 141 | + | func (m MergeRequest) IsOpen() bool { return m.State == MRStateOpen } | |||
| 142 | + | ||||
| 143 | + | // Comment is a note on a merge request, general or on a line of its diff. | |||
| 144 | + | type Comment struct { | |||
| 145 | + | // ID is unique within the merge request, in order of creation. | |||
| 146 | + | ID int64 | |||
| 147 | + | Author string | |||
| 148 | + | // Path, Line and Side are empty for a general comment. | |||
| 149 | + | Path string | |||
| 150 | + | Line int | |||
| 151 | + | Side DiffSide | |||
| 152 | + | // CommitID is the head of the request when the comment was written; zero | |||
| 153 | + | // when the source branch was gone. | |||
| 154 | + | CommitID ObjectID | |||
| 155 | + | Text PlainText | |||
| 156 | + | Resolved bool | |||
| 157 | + | CreatedAt time.Time | |||
| 158 | + | // Version is set by the store on read, like MergeRequest.Version. | |||
| 159 | + | Version uint64 | |||
| 160 | + | } | |||
| 161 | + | ||||
| 162 | + | // IsGeneral reports whether the comment is not attached to a line. | |||
| 163 | + | func (c Comment) IsGeneral() bool { return c.Path == "" } | |||
| 164 | + | ||||
| 165 | + | // HasLine reports whether the diff has a line numbered line on side in the | |||
| 166 | + | // file path: the new path of a file, or the old path of a deleted one. The new | |||
| 167 | + | // side holds the context and addition lines, the old side the context and | |||
| 168 | + | // deletion lines. A file the diff truncated has no lines. | |||
| 169 | + | func (d Diff) HasLine(path string, side DiffSide, line int) bool { | |||
| 170 | + | if path == "" || line < 1 { | |||
| 171 | + | return false | |||
| 172 | + | } | |||
| 173 | + | for _, f := range d.Files { | |||
| 174 | + | name := f.NewPath | |||
| 175 | + | if f.Status == FileDeleted { | |||
| 176 | + | name = f.OldPath | |||
| 177 | + | } | |||
| 178 | + | if name != path { | |||
| 179 | + | continue | |||
| 180 | + | } | |||
| 181 | + | for _, h := range f.Hunks { | |||
| 182 | + | for _, l := range h.Lines { | |||
| 183 | + | switch { | |||
| 184 | + | case side == SideNew && l.Kind != LineDeletion && l.NewNumber == line: | |||
| 185 | + | return true | |||
| 186 | + | case side == SideOld && l.Kind != LineAddition && l.OldNumber == line: | |||
| 187 | + | return true | |||
| 188 | + | } | |||
| 189 | + | } | |||
| 190 | + | } | |||
| 191 | + | } | |||
| 192 | + | return false | |||
| 193 | + | } | |||
| 194 | + | ||||
| 195 | + | // Identity is a name and an email address, such as the committer of the | |||
| 196 | + | // commits ermite creates. | |||
| 197 | + | type Identity struct { | |||
| 198 | + | Name, Email string | |||
| 199 | + | } | |||
| 200 | + | ||||
| 201 | + | // SquashCommit is the commit a squash merge creates: the tree of the head on | |||
| 202 | + | // top of the target. | |||
| 203 | + | type SquashCommit struct { | |||
| 204 | + | Tree, Parent ObjectID | |||
| 205 | + | Author, Committer Signature | |||
| 206 | + | Message string | |||
| 207 | + | } | |||
| 208 | + | ||||
| 209 | + | // MaxSquashCommits bounds the commits a squash lists in its message. | |||
| 210 | + | const MaxSquashCommits = 1000 | |||
| 211 | + | ||||
| 212 | + | // NewSquashCommit builds the squash commit of a merge request (ADR 0022). | |||
| 213 | + | // commits are the commits of the branch that the target lacks, newest first, | |||
| 214 | + | // as Git lists them, and not empty. The author is the author of the oldest | |||
| 215 | + | // one, with its date; the other distinct authors become Co-authored-by | |||
| 216 | + | // trailers, in order of appearance. The message is the title, " (!<number>)", | |||
| 217 | + | // a blank line, the summaries of the commits oldest first as a list, and the | |||
| 218 | + | // trailers. | |||
| 219 | + | func NewSquashCommit(mr MergeRequest, tree, parent ObjectID, commits []Commit, committer Identity, at time.Time) SquashCommit { | |||
| 220 | + | oldest := slices.Clone(commits) | |||
| 221 | + | slices.Reverse(oldest) | |||
| 222 | + | var first Signature | |||
| 223 | + | if len(oldest) > 0 { | |||
| 224 | + | first = oldest[0].Author | |||
| 225 | + | } | |||
| 226 | + | ||||
| 227 | + | var msg strings.Builder | |||
| 228 | + | fmt.Fprintf(&msg, "%s (!%d)\n", mr.Title.String(), mr.Number) | |||
| 229 | + | if len(oldest) > 0 { | |||
| 230 | + | msg.WriteString("\n") | |||
| 231 | + | } | |||
| 232 | + | var trailers []string | |||
| 233 | + | seen := map[string]bool{strings.ToLower(first.Email): true} | |||
| 234 | + | for _, c := range oldest { | |||
| 235 | + | msg.WriteString("- " + singleLine(c.Summary) + "\n") | |||
| 236 | + | key := strings.ToLower(c.Author.Email) | |||
| 237 | + | if !seen[key] { | |||
| 238 | + | seen[key] = true | |||
| 239 | + | trailers = append(trailers, "Co-authored-by: "+identLine(c.Author.Name, c.Author.Email)) | |||
| 240 | + | } | |||
| 241 | + | } | |||
| 242 | + | if len(trailers) > 0 { | |||
| 243 | + | msg.WriteString("\n" + strings.Join(trailers, "\n") + "\n") | |||
| 244 | + | } | |||
| 245 | + | return SquashCommit{ | |||
| 246 | + | Tree: tree, Parent: parent, | |||
| 247 | + | Author: first, | |||
| 248 | + | Committer: Signature{Name: committer.Name, Email: committer.Email, Time: at.UTC()}, | |||
| 249 | + | Message: msg.String(), | |||
| 250 | + | } | |||
| 251 | + | } | |||
| 252 | + | ||||
| 253 | + | // Payload returns the text of the commit object without a signature, which is | |||
| 254 | + | // what the signature covers. | |||
| 255 | + | func (c SquashCommit) Payload() []byte { return c.encode("") } | |||
| 256 | + | ||||
| 257 | + | // Signed returns the text of the commit object with the armored SSH signature | |||
| 258 | + | // in its gpgsig header. | |||
| 259 | + | func (c SquashCommit) Signed(armored string) []byte { return c.encode(armored) } | |||
| 260 | + | ||||
| 261 | + | func (c SquashCommit) encode(armored string) []byte { | |||
| 262 | + | var b strings.Builder | |||
| 263 | + | b.WriteString("tree " + c.Tree.String() + "\n") | |||
| 264 | + | b.WriteString("parent " + c.Parent.String() + "\n") | |||
| 265 | + | b.WriteString("author " + signatureLine(c.Author) + "\n") | |||
| 266 | + | b.WriteString("committer " + signatureLine(c.Committer) + "\n") | |||
| 267 | + | if armored != "" { | |||
| 268 | + | lines := strings.Split(strings.TrimRight(armored, "\n"), "\n") | |||
| 269 | + | b.WriteString("gpgsig " + lines[0] + "\n") | |||
| 270 | + | for _, l := range lines[1:] { | |||
| 271 | + | b.WriteString(" " + l + "\n") | |||
| 272 | + | } | |||
| 273 | + | } | |||
| 274 | + | b.WriteString("\n") | |||
| 275 | + | b.WriteString(c.Message) | |||
| 276 | + | return []byte(b.String()) | |||
| 277 | + | } | |||
| 278 | + | ||||
| 279 | + | func signatureLine(s Signature) string { | |||
| 280 | + | return fmt.Sprintf("%s %d +0000", identLine(s.Name, s.Email), s.Time.Unix()) | |||
| 281 | + | } | |||
| 282 | + | ||||
| 283 | + | // identLine formats "Name <email>", dropping the characters Git does not | |||
| 284 | + | // allow in an identity. | |||
| 285 | + | func identLine(name, email string) string { | |||
| 286 | + | clean := func(s string) string { | |||
| 287 | + | return strings.TrimSpace(strings.Map(func(r rune) rune { | |||
| 288 | + | if r == '<' || r == '>' || r < 0x20 || r == 0x7f { | |||
| 289 | + | return -1 | |||
| 290 | + | } | |||
| 291 | + | return r | |||
| 292 | + | }, s)) | |||
| 293 | + | } | |||
| 294 | + | return clean(name) + " <" + clean(email) + ">" | |||
| 295 | + | } | |||
| 296 | + | ||||
| 297 | + | // singleLine replaces the control characters of a commit summary, so that it | |||
| 298 | + | // stays one line of the message. | |||
| 299 | + | func singleLine(s string) string { | |||
| 300 | + | return strings.Map(func(r rune) rune { | |||
| 301 | + | if r < 0x20 || r == 0x7f { | |||
| 302 | + | return ' ' | |||
| 303 | + | } | |||
| 304 | + | return r | |||
| 305 | + | }, s) | |||
| 306 | + | } | |||
A internal/domain/mergerequest_test.go +203 -0
| @@ -0,0 +1,203 @@ | |||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | ||
| 3 | + | package domain_test | |
| 4 | + | ||
| 5 | + | import ( | |
| 6 | + | "strings" | |
| 7 | + | "testing" | |
| 8 | + | "time" | |
| 9 | + | ||
| 10 | + | "github.com/stretchr/testify/assert" | |
| 11 | + | "github.com/stretchr/testify/require" | |
| 12 | + | ||
| 13 | + | "bnema.dev/ermite/internal/domain" | |
| 14 | + | ) | |
| 15 | + | ||
| 16 | + | func oid(t *testing.T, c byte) domain.ObjectID { | |
| 17 | + | t.Helper() | |
| 18 | + | id, err := domain.ParseObjectID(strings.Repeat(string(c), domain.ObjectIDLen)) | |
| 19 | + | require.NoError(t, err) | |
| 20 | + | return id | |
| 21 | + | } | |
| 22 | + | ||
| 23 | + | func TestDefaultRepoSettings(t *testing.T) { | |
| 24 | + | s := domain.DefaultRepoSettings() | |
| 25 | + | assert.Equal(t, []domain.MergeMode{domain.MergeModeFastForward, domain.MergeModeSquash}, s.AllowedMergeModes) | |
| 26 | + | assert.True(t, s.FailingChecksBlock) | |
| 27 | + | assert.True(t, s.Allows(domain.MergeModeSquash)) | |
| 28 | + | assert.False(t, domain.RepoSettings{}.Allows(domain.MergeModeSquash)) | |
| 29 | + | assert.True(t, domain.RepoSettings{}.IsZero()) | |
| 30 | + | assert.False(t, s.IsZero()) | |
| 31 | + | ||
| 32 | + | // The default list is a copy: callers cannot alter the next default. | |
| 33 | + | s.AllowedMergeModes[0] = domain.MergeModeSquash | |
| 34 | + | assert.Equal(t, domain.MergeModeFastForward, domain.DefaultRepoSettings().AllowedMergeModes[0]) | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | func TestNormalizeMergeModes(t *testing.T) { | |
| 38 | + | got, err := domain.NormalizeMergeModes([]domain.MergeMode{domain.MergeModeSquash, domain.MergeModeFastForward, domain.MergeModeSquash}) | |
| 39 | + | require.NoError(t, err) | |
| 40 | + | assert.Equal(t, domain.MergeModes(), got) | |
| 41 | + | ||
| 42 | + | got, err = domain.NormalizeMergeModes([]domain.MergeMode{domain.MergeModeSquash}) | |
| 43 | + | require.NoError(t, err) | |
| 44 | + | assert.Equal(t, []domain.MergeMode{domain.MergeModeSquash}, got) | |
| 45 | + | ||
| 46 | + | _, err = domain.NormalizeMergeModes(nil) | |
| 47 | + | assert.ErrorIs(t, err, domain.ErrInvalid) | |
| 48 | + | _, err = domain.NormalizeMergeModes([]domain.MergeMode{"rebase"}) | |
| 49 | + | assert.ErrorIs(t, err, domain.ErrInvalid) | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | func TestParseMergeMode(t *testing.T) { | |
| 53 | + | m, err := domain.ParseMergeMode("fast-forward") | |
| 54 | + | require.NoError(t, err) | |
| 55 | + | assert.Equal(t, domain.MergeModeFastForward, m) | |
| 56 | + | _, err = domain.ParseMergeMode("merge") | |
| 57 | + | assert.ErrorIs(t, err, domain.ErrInvalid) | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | func hunkDiff() domain.Diff { | |
| 61 | + | return domain.Diff{Files: []domain.FileDiff{ | |
| 62 | + | {OldPath: "a.txt", NewPath: "a.txt", Status: domain.FileModified, Hunks: []domain.Hunk{{ | |
| 63 | + | Lines: []domain.Line{ | |
| 64 | + | {Kind: domain.LineContext, OldNumber: 4, NewNumber: 4}, | |
| 65 | + | {Kind: domain.LineDeletion, OldNumber: 5}, | |
| 66 | + | {Kind: domain.LineAddition, NewNumber: 5}, | |
| 67 | + | {Kind: domain.LineAddition, NewNumber: 6}, | |
| 68 | + | }, | |
| 69 | + | }}}, | |
| 70 | + | {OldPath: "gone.txt", Status: domain.FileDeleted, Hunks: []domain.Hunk{{ | |
| 71 | + | Lines: []domain.Line{{Kind: domain.LineDeletion, OldNumber: 1}}, | |
| 72 | + | }}}, | |
| 73 | + | {NewPath: "big.txt", Status: domain.FileAdded, Truncated: true}, | |
| 74 | + | {OldPath: "old.txt", NewPath: "new.txt", Status: domain.FileRenamed, Hunks: []domain.Hunk{{ | |
| 75 | + | Lines: []domain.Line{{Kind: domain.LineAddition, NewNumber: 2}}, | |
| 76 | + | }}}, | |
| 77 | + | }} | |
| 78 | + | } | |
| 79 | + | ||
| 80 | + | func TestDiffHasLine(t *testing.T) { | |
| 81 | + | d := hunkDiff() | |
| 82 | + | tests := []struct { | |
| 83 | + | name string | |
| 84 | + | path string | |
| 85 | + | side domain.DiffSide | |
| 86 | + | line int | |
| 87 | + | want bool | |
| 88 | + | }{ | |
| 89 | + | {"context on the new side", "a.txt", domain.SideNew, 4, true}, | |
| 90 | + | {"context on the old side", "a.txt", domain.SideOld, 4, true}, | |
| 91 | + | {"deletion on the old side", "a.txt", domain.SideOld, 5, true}, | |
| 92 | + | {"addition on the new side", "a.txt", domain.SideNew, 6, true}, | |
| 93 | + | {"addition is not on the old side", "a.txt", domain.SideOld, 6, false}, | |
| 94 | + | {"new line 5 is the addition, not the deletion", "a.txt", domain.SideNew, 5, true}, | |
| 95 | + | {"line outside the hunk", "a.txt", domain.SideNew, 99, false}, | |
| 96 | + | {"line zero", "a.txt", domain.SideNew, 0, false}, | |
| 97 | + | {"negative line", "a.txt", domain.SideNew, -1, false}, | |
| 98 | + | {"no side", "a.txt", domain.SideNone, 4, false}, | |
| 99 | + | {"unknown path", "nope.txt", domain.SideNew, 4, false}, | |
| 100 | + | {"empty path", "", domain.SideNew, 4, false}, | |
| 101 | + | {"deleted file by its old path", "gone.txt", domain.SideOld, 1, true}, | |
| 102 | + | {"deleted file has no new side", "gone.txt", domain.SideNew, 1, false}, | |
| 103 | + | {"truncated file has no lines", "big.txt", domain.SideNew, 1, false}, | |
| 104 | + | {"renamed file by its new path", "new.txt", domain.SideNew, 2, true}, | |
| 105 | + | {"renamed file by its old path", "old.txt", domain.SideNew, 2, false}, | |
| 106 | + | } | |
| 107 | + | for _, tc := range tests { | |
| 108 | + | t.Run(tc.name, func(t *testing.T) { | |
| 109 | + | assert.Equal(t, tc.want, d.HasLine(tc.path, tc.side, tc.line)) | |
| 110 | + | }) | |
| 111 | + | } | |
| 112 | + | } | |
| 113 | + | ||
| 114 | + | func sig(name, email string, sec int64) domain.Signature { | |
| 115 | + | return domain.Signature{Name: name, Email: email, Time: time.Unix(sec, 0).UTC()} | |
| 116 | + | } | |
| 117 | + | ||
| 118 | + | func TestNewSquashCommit(t *testing.T) { | |
| 119 | + | title, err := domain.ParseMRTitle("Add search") | |
| 120 | + | require.NoError(t, err) | |
| 121 | + | mr := domain.MergeRequest{Number: 7, Title: title} | |
| 122 | + | // Newest first, as Git lists them. | |
| 123 | + | commits := []domain.Commit{ | |
| 124 | + | {Summary: "fix typo", Author: sig("Bob", "bob@example.org", 300)}, | |
| 125 | + | {Summary: "add tests", Author: sig("Ada", "ADA@example.org", 200)}, | |
| 126 | + | {Summary: "add search", Author: sig("Ada", "ada@example.org", 100)}, | |
| 127 | + | {Summary: "wip", Author: sig("Cy", "cy@example.org", 150)}, | |
| 128 | + | {Summary: "polish", Author: sig("Bob", "bob@example.org", 160)}, | |
| 129 | + | } | |
| 130 | + | committer := domain.Identity{Name: "ermite", Email: "ermite@git.example.org"} | |
| 131 | + | at := time.Unix(1_800_000_000, 0) | |
| 132 | + | ||
| 133 | + | c := domain.NewSquashCommit(mr, oid(t, 'a'), oid(t, 'b'), commits, committer, at) | |
| 134 | + | ||
| 135 | + | // The author is the author of the oldest commit (the last one listed). | |
| 136 | + | assert.Equal(t, sig("Bob", "bob@example.org", 160), c.Author) | |
| 137 | + | assert.Equal(t, domain.Signature{Name: "ermite", Email: "ermite@git.example.org", Time: at.UTC()}, c.Committer) | |
| 138 | + | assert.Equal(t, "Add search (!7)\n"+ | |
| 139 | + | "\n"+ | |
| 140 | + | "- polish\n- wip\n- add search\n- add tests\n- fix typo\n"+ | |
| 141 | + | "\n"+ | |
| 142 | + | "Co-authored-by: Cy <cy@example.org>\n"+ | |
| 143 | + | "Co-authored-by: Ada <ada@example.org>\n", c.Message) | |
| 144 | + | ||
| 145 | + | want := "tree " + strings.Repeat("a", 40) + "\n" + | |
| 146 | + | "parent " + strings.Repeat("b", 40) + "\n" + | |
| 147 | + | "author Bob <bob@example.org> 160 +0000\n" + | |
| 148 | + | "committer ermite <ermite@git.example.org> 1800000000 +0000\n" + | |
| 149 | + | "\n" + c.Message | |
| 150 | + | assert.Equal(t, want, string(c.Payload())) | |
| 151 | + | } | |
| 152 | + | ||
| 153 | + | func TestSquashCommitSigned(t *testing.T) { | |
| 154 | + | title, err := domain.ParseMRTitle("T") | |
| 155 | + | require.NoError(t, err) | |
| 156 | + | c := domain.NewSquashCommit(domain.MergeRequest{Number: 1, Title: title}, oid(t, 'a'), oid(t, 'b'), | |
| 157 | + | []domain.Commit{{Summary: "s", Author: sig("A", "a@x.org", 1)}}, domain.Identity{Name: "e", Email: "e@x.org"}, time.Unix(2, 0)) | |
| 158 | + | ||
| 159 | + | got := string(c.Signed("-----BEGIN SSH SIGNATURE-----\nAAAA\nBBBB\n-----END SSH SIGNATURE-----\n")) | |
| 160 | + | assert.Equal(t, "tree "+strings.Repeat("a", 40)+"\n"+ | |
| 161 | + | "parent "+strings.Repeat("b", 40)+"\n"+ | |
| 162 | + | "author A <a@x.org> 1 +0000\n"+ | |
| 163 | + | "committer e <e@x.org> 2 +0000\n"+ | |
| 164 | + | "gpgsig -----BEGIN SSH SIGNATURE-----\n"+ | |
| 165 | + | " AAAA\n"+ | |
| 166 | + | " BBBB\n"+ | |
| 167 | + | " -----END SSH SIGNATURE-----\n"+ | |
| 168 | + | "\n"+c.Message, got) | |
| 169 | + | } | |
| 170 | + | ||
| 171 | + | func TestSquashCommitCleansIdentitiesAndSummaries(t *testing.T) { | |
| 172 | + | title, err := domain.ParseMRTitle("T") | |
| 173 | + | require.NoError(t, err) | |
| 174 | + | commits := []domain.Commit{ | |
| 175 | + | {Summary: "second", Author: sig("Eve <evil@x.org>\nCo-authored-by: Z", "eve@x.org", 2)}, | |
| 176 | + | {Summary: "first\x1b[0m", Author: sig("Ada", "ada@x.org", 1)}, | |
| 177 | + | } | |
| 178 | + | c := domain.NewSquashCommit(domain.MergeRequest{Number: 2, Title: title}, oid(t, 'a'), oid(t, 'b'), commits, | |
| 179 | + | domain.Identity{Name: "e", Email: "e@x.org"}, time.Unix(3, 0)) | |
| 180 | + | assert.Contains(t, c.Message, "- first [0m\n") | |
| 181 | + | assert.Contains(t, c.Message, "\nCo-authored-by: Eve evil@x.orgCo-authored-by: Z <eve@x.org>\n") | |
| 182 | + | assert.NotContains(t, c.Message, "\nCo-authored-by: Z") | |
| 183 | + | } | |
| 184 | + | ||
| 185 | + | func TestSigningKeyInfoAllowedSigners(t *testing.T) { | |
| 186 | + | i := domain.SigningKeyInfo{ | |
| 187 | + | Key: domain.SigningPublicKey{AuthorizedKey: "ssh-ed25519 AAAA"}, | |
| 188 | + | Committer: domain.Identity{Name: "ermite", Email: "ermite@git.example.org"}, | |
| 189 | + | } | |
| 190 | + | assert.Equal(t, "ermite@git.example.org ssh-ed25519 AAAA", i.AllowedSigners()) | |
| 191 | + | } | |
| 192 | + | ||
| 193 | + | func TestParseBranchName(t *testing.T) { | |
| 194 | + | for _, ok := range []string{"main", "feat/x", "v1.2", "a-b_c"} { | |
| 195 | + | got, err := domain.ParseBranchName(ok) | |
| 196 | + | require.NoError(t, err, ok) | |
| 197 | + | assert.Equal(t, ok, got) | |
| 198 | + | } | |
| 199 | + | for _, bad := range []string{"", "refs/heads/main", "-x", "a..b", "a b", "x/", "a:b", "x.lock", "a\x00b"} { | |
| 200 | + | _, err := domain.ParseBranchName(bad) | |
| 201 | + | assert.ErrorIs(t, err, domain.ErrInvalidRevision, "%q", bad) | |
| 202 | + | } | |
| 203 | + | } | |
| @@ -0,0 +1,203 @@ | |||||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |||
| 2 | + | ||||
| 3 | + | package domain_test | |||
| 4 | + | ||||
| 5 | + | import ( | |||
| 6 | + | "strings" | |||
| 7 | + | "testing" | |||
| 8 | + | "time" | |||
| 9 | + | ||||
| 10 | + | "github.com/stretchr/testify/assert" | |||
| 11 | + | "github.com/stretchr/testify/require" | |||
| 12 | + | ||||
| 13 | + | "bnema.dev/ermite/internal/domain" | |||
| 14 | + | ) | |||
| 15 | + | ||||
| 16 | + | func oid(t *testing.T, c byte) domain.ObjectID { | |||
| 17 | + | t.Helper() | |||
| 18 | + | id, err := domain.ParseObjectID(strings.Repeat(string(c), domain.ObjectIDLen)) | |||
| 19 | + | require.NoError(t, err) | |||
| 20 | + | return id | |||
| 21 | + | } | |||
| 22 | + | ||||
| 23 | + | func TestDefaultRepoSettings(t *testing.T) { | |||
| 24 | + | s := domain.DefaultRepoSettings() | |||
| 25 | + | assert.Equal(t, []domain.MergeMode{domain.MergeModeFastForward, domain.MergeModeSquash}, s.AllowedMergeModes) | |||
| 26 | + | assert.True(t, s.FailingChecksBlock) | |||
| 27 | + | assert.True(t, s.Allows(domain.MergeModeSquash)) | |||
| 28 | + | assert.False(t, domain.RepoSettings{}.Allows(domain.MergeModeSquash)) | |||
| 29 | + | assert.True(t, domain.RepoSettings{}.IsZero()) | |||
| 30 | + | assert.False(t, s.IsZero()) | |||
| 31 | + | ||||
| 32 | + | // The default list is a copy: callers cannot alter the next default. | |||
| 33 | + | s.AllowedMergeModes[0] = domain.MergeModeSquash | |||
| 34 | + | assert.Equal(t, domain.MergeModeFastForward, domain.DefaultRepoSettings().AllowedMergeModes[0]) | |||
| 35 | + | } | |||
| 36 | + | ||||
| 37 | + | func TestNormalizeMergeModes(t *testing.T) { | |||
| 38 | + | got, err := domain.NormalizeMergeModes([]domain.MergeMode{domain.MergeModeSquash, domain.MergeModeFastForward, domain.MergeModeSquash}) | |||
| 39 | + | require.NoError(t, err) | |||
| 40 | + | assert.Equal(t, domain.MergeModes(), got) | |||
| 41 | + | ||||
| 42 | + | got, err = domain.NormalizeMergeModes([]domain.MergeMode{domain.MergeModeSquash}) | |||
| 43 | + | require.NoError(t, err) | |||
| 44 | + | assert.Equal(t, []domain.MergeMode{domain.MergeModeSquash}, got) | |||
| 45 | + | ||||
| 46 | + | _, err = domain.NormalizeMergeModes(nil) | |||
| 47 | + | assert.ErrorIs(t, err, domain.ErrInvalid) | |||
| 48 | + | _, err = domain.NormalizeMergeModes([]domain.MergeMode{"rebase"}) | |||
| 49 | + | assert.ErrorIs(t, err, domain.ErrInvalid) | |||
| 50 | + | } | |||
| 51 | + | ||||
| 52 | + | func TestParseMergeMode(t *testing.T) { | |||
| 53 | + | m, err := domain.ParseMergeMode("fast-forward") | |||
| 54 | + | require.NoError(t, err) | |||
| 55 | + | assert.Equal(t, domain.MergeModeFastForward, m) | |||
| 56 | + | _, err = domain.ParseMergeMode("merge") | |||
| 57 | + | assert.ErrorIs(t, err, domain.ErrInvalid) | |||
| 58 | + | } | |||
| 59 | + | ||||
| 60 | + | func hunkDiff() domain.Diff { | |||
| 61 | + | return domain.Diff{Files: []domain.FileDiff{ | |||
| 62 | + | {OldPath: "a.txt", NewPath: "a.txt", Status: domain.FileModified, Hunks: []domain.Hunk{{ | |||
| 63 | + | Lines: []domain.Line{ | |||
| 64 | + | {Kind: domain.LineContext, OldNumber: 4, NewNumber: 4}, | |||
| 65 | + | {Kind: domain.LineDeletion, OldNumber: 5}, | |||
| 66 | + | {Kind: domain.LineAddition, NewNumber: 5}, | |||
| 67 | + | {Kind: domain.LineAddition, NewNumber: 6}, | |||
| 68 | + | }, | |||
| 69 | + | }}}, | |||
| 70 | + | {OldPath: "gone.txt", Status: domain.FileDeleted, Hunks: []domain.Hunk{{ | |||
| 71 | + | Lines: []domain.Line{{Kind: domain.LineDeletion, OldNumber: 1}}, | |||
| 72 | + | }}}, | |||
| 73 | + | {NewPath: "big.txt", Status: domain.FileAdded, Truncated: true}, | |||
| 74 | + | {OldPath: "old.txt", NewPath: "new.txt", Status: domain.FileRenamed, Hunks: []domain.Hunk{{ | |||
| 75 | + | Lines: []domain.Line{{Kind: domain.LineAddition, NewNumber: 2}}, | |||
| 76 | + | }}}, | |||
| 77 | + | }} | |||
| 78 | + | } | |||
| 79 | + | ||||
| 80 | + | func TestDiffHasLine(t *testing.T) { | |||
| 81 | + | d := hunkDiff() | |||
| 82 | + | tests := []struct { | |||
| 83 | + | name string | |||
| 84 | + | path string | |||
| 85 | + | side domain.DiffSide | |||
| 86 | + | line int | |||
| 87 | + | want bool | |||
| 88 | + | }{ | |||
| 89 | + | {"context on the new side", "a.txt", domain.SideNew, 4, true}, | |||
| 90 | + | {"context on the old side", "a.txt", domain.SideOld, 4, true}, | |||
| 91 | + | {"deletion on the old side", "a.txt", domain.SideOld, 5, true}, | |||
| 92 | + | {"addition on the new side", "a.txt", domain.SideNew, 6, true}, | |||
| 93 | + | {"addition is not on the old side", "a.txt", domain.SideOld, 6, false}, | |||
| 94 | + | {"new line 5 is the addition, not the deletion", "a.txt", domain.SideNew, 5, true}, | |||
| 95 | + | {"line outside the hunk", "a.txt", domain.SideNew, 99, false}, | |||
| 96 | + | {"line zero", "a.txt", domain.SideNew, 0, false}, | |||
| 97 | + | {"negative line", "a.txt", domain.SideNew, -1, false}, | |||
| 98 | + | {"no side", "a.txt", domain.SideNone, 4, false}, | |||
| 99 | + | {"unknown path", "nope.txt", domain.SideNew, 4, false}, | |||
| 100 | + | {"empty path", "", domain.SideNew, 4, false}, | |||
| 101 | + | {"deleted file by its old path", "gone.txt", domain.SideOld, 1, true}, | |||
| 102 | + | {"deleted file has no new side", "gone.txt", domain.SideNew, 1, false}, | |||
| 103 | + | {"truncated file has no lines", "big.txt", domain.SideNew, 1, false}, | |||
| 104 | + | {"renamed file by its new path", "new.txt", domain.SideNew, 2, true}, | |||
| 105 | + | {"renamed file by its old path", "old.txt", domain.SideNew, 2, false}, | |||
| 106 | + | } | |||
| 107 | + | for _, tc := range tests { | |||
| 108 | + | t.Run(tc.name, func(t *testing.T) { | |||
| 109 | + | assert.Equal(t, tc.want, d.HasLine(tc.path, tc.side, tc.line)) | |||
| 110 | + | }) | |||
| 111 | + | } | |||
| 112 | + | } | |||
| 113 | + | ||||
| 114 | + | func sig(name, email string, sec int64) domain.Signature { | |||
| 115 | + | return domain.Signature{Name: name, Email: email, Time: time.Unix(sec, 0).UTC()} | |||
| 116 | + | } | |||
| 117 | + | ||||
| 118 | + | func TestNewSquashCommit(t *testing.T) { | |||
| 119 | + | title, err := domain.ParseMRTitle("Add search") | |||
| 120 | + | require.NoError(t, err) | |||
| 121 | + | mr := domain.MergeRequest{Number: 7, Title: title} | |||
| 122 | + | // Newest first, as Git lists them. | |||
| 123 | + | commits := []domain.Commit{ | |||
| 124 | + | {Summary: "fix typo", Author: sig("Bob", "bob@example.org", 300)}, | |||
| 125 | + | {Summary: "add tests", Author: sig("Ada", "ADA@example.org", 200)}, | |||
| 126 | + | {Summary: "add search", Author: sig("Ada", "ada@example.org", 100)}, | |||
| 127 | + | {Summary: "wip", Author: sig("Cy", "cy@example.org", 150)}, | |||
| 128 | + | {Summary: "polish", Author: sig("Bob", "bob@example.org", 160)}, | |||
| 129 | + | } | |||
| 130 | + | committer := domain.Identity{Name: "ermite", Email: "ermite@git.example.org"} | |||
| 131 | + | at := time.Unix(1_800_000_000, 0) | |||
| 132 | + | ||||
| 133 | + | c := domain.NewSquashCommit(mr, oid(t, 'a'), oid(t, 'b'), commits, committer, at) | |||
| 134 | + | ||||
| 135 | + | // The author is the author of the oldest commit (the last one listed). | |||
| 136 | + | assert.Equal(t, sig("Bob", "bob@example.org", 160), c.Author) | |||
| 137 | + | assert.Equal(t, domain.Signature{Name: "ermite", Email: "ermite@git.example.org", Time: at.UTC()}, c.Committer) | |||
| 138 | + | assert.Equal(t, "Add search (!7)\n"+ | |||
| 139 | + | "\n"+ | |||
| 140 | + | "- polish\n- wip\n- add search\n- add tests\n- fix typo\n"+ | |||
| 141 | + | "\n"+ | |||
| 142 | + | "Co-authored-by: Cy <cy@example.org>\n"+ | |||
| 143 | + | "Co-authored-by: Ada <ada@example.org>\n", c.Message) | |||
| 144 | + | ||||
| 145 | + | want := "tree " + strings.Repeat("a", 40) + "\n" + | |||
| 146 | + | "parent " + strings.Repeat("b", 40) + "\n" + | |||
| 147 | + | "author Bob <bob@example.org> 160 +0000\n" + | |||
| 148 | + | "committer ermite <ermite@git.example.org> 1800000000 +0000\n" + | |||
| 149 | + | "\n" + c.Message | |||
| 150 | + | assert.Equal(t, want, string(c.Payload())) | |||
| 151 | + | } | |||
| 152 | + | ||||
| 153 | + | func TestSquashCommitSigned(t *testing.T) { | |||
| 154 | + | title, err := domain.ParseMRTitle("T") | |||
| 155 | + | require.NoError(t, err) | |||
| 156 | + | c := domain.NewSquashCommit(domain.MergeRequest{Number: 1, Title: title}, oid(t, 'a'), oid(t, 'b'), | |||
| 157 | + | []domain.Commit{{Summary: "s", Author: sig("A", "a@x.org", 1)}}, domain.Identity{Name: "e", Email: "e@x.org"}, time.Unix(2, 0)) | |||
| 158 | + | ||||
| 159 | + | got := string(c.Signed("-----BEGIN SSH SIGNATURE-----\nAAAA\nBBBB\n-----END SSH SIGNATURE-----\n")) | |||
| 160 | + | assert.Equal(t, "tree "+strings.Repeat("a", 40)+"\n"+ | |||
| 161 | + | "parent "+strings.Repeat("b", 40)+"\n"+ | |||
| 162 | + | "author A <a@x.org> 1 +0000\n"+ | |||
| 163 | + | "committer e <e@x.org> 2 +0000\n"+ | |||
| 164 | + | "gpgsig -----BEGIN SSH SIGNATURE-----\n"+ | |||
| 165 | + | " AAAA\n"+ | |||
| 166 | + | " BBBB\n"+ | |||
| 167 | + | " -----END SSH SIGNATURE-----\n"+ | |||
| 168 | + | "\n"+c.Message, got) | |||
| 169 | + | } | |||
| 170 | + | ||||
| 171 | + | func TestSquashCommitCleansIdentitiesAndSummaries(t *testing.T) { | |||
| 172 | + | title, err := domain.ParseMRTitle("T") | |||
| 173 | + | require.NoError(t, err) | |||
| 174 | + | commits := []domain.Commit{ | |||
| 175 | + | {Summary: "second", Author: sig("Eve <evil@x.org>\nCo-authored-by: Z", "eve@x.org", 2)}, | |||
| 176 | + | {Summary: "first\x1b[0m", Author: sig("Ada", "ada@x.org", 1)}, | |||
| 177 | + | } | |||
| 178 | + | c := domain.NewSquashCommit(domain.MergeRequest{Number: 2, Title: title}, oid(t, 'a'), oid(t, 'b'), commits, | |||
| 179 | + | domain.Identity{Name: "e", Email: "e@x.org"}, time.Unix(3, 0)) | |||
| 180 | + | assert.Contains(t, c.Message, "- first [0m\n") | |||
| 181 | + | assert.Contains(t, c.Message, "\nCo-authored-by: Eve evil@x.orgCo-authored-by: Z <eve@x.org>\n") | |||
| 182 | + | assert.NotContains(t, c.Message, "\nCo-authored-by: Z") | |||
| 183 | + | } | |||
| 184 | + | ||||
| 185 | + | func TestSigningKeyInfoAllowedSigners(t *testing.T) { | |||
| 186 | + | i := domain.SigningKeyInfo{ | |||
| 187 | + | Key: domain.SigningPublicKey{AuthorizedKey: "ssh-ed25519 AAAA"}, | |||
| 188 | + | Committer: domain.Identity{Name: "ermite", Email: "ermite@git.example.org"}, | |||
| 189 | + | } | |||
| 190 | + | assert.Equal(t, "ermite@git.example.org ssh-ed25519 AAAA", i.AllowedSigners()) | |||
| 191 | + | } | |||
| 192 | + | ||||
| 193 | + | func TestParseBranchName(t *testing.T) { | |||
| 194 | + | for _, ok := range []string{"main", "feat/x", "v1.2", "a-b_c"} { | |||
| 195 | + | got, err := domain.ParseBranchName(ok) | |||
| 196 | + | require.NoError(t, err, ok) | |||
| 197 | + | assert.Equal(t, ok, got) | |||
| 198 | + | } | |||
| 199 | + | for _, bad := range []string{"", "refs/heads/main", "-x", "a..b", "a b", "x/", "a:b", "x.lock", "a\x00b"} { | |||
| 200 | + | _, err := domain.ParseBranchName(bad) | |||
| 201 | + | assert.ErrorIs(t, err, domain.ErrInvalidRevision, "%q", bad) | |||
| 202 | + | } | |||
| 203 | + | } | |||
M internal/domain/principal.go +6 -0
| @@ -25,6 +25,7 @@ type Principal struct { | |||
| 25 | 25 | scopes []Scope | |
| 26 | 26 | repos []RepoName | |
| 27 | 27 | expiresAt time.Time | |
| 28 | + | label string | |
| 28 | 29 | } | |
| 29 | 30 | ||
| 30 | 31 | // Anonymous returns the principal of a caller without credentials. | |
| @@ -41,6 +42,7 @@ func TokenPrincipal(t Token) Principal { | |||
| 41 | 42 | scopes: slices.Clone(t.Scopes), | |
| 42 | 43 | repos: slices.Clone(t.Repos), | |
| 43 | 44 | expiresAt: t.ExpiresAt, | |
| 45 | + | label: t.Label, | |
| 44 | 46 | } | |
| 45 | 47 | } | |
| 46 | 48 | ||
| @@ -98,3 +100,7 @@ func (p Principal) Allows(s Scope, repo RepoName) bool { | |||
| 98 | 100 | } | |
| 99 | 101 | return true | |
| 100 | 102 | } | |
| 103 | + | ||
| 104 | + | // TokenLabel returns the label of the stored token, or "" for other | |
| 105 | + | // principals. | |
| 106 | + | func (p Principal) TokenLabel() string { return p.label } | |
| @@ -25,6 +25,7 @@ type Principal struct { | |||||
| 25 | scopes []Scope | 25 | scopes []Scope | ||
| 26 | repos []RepoName | 26 | repos []RepoName | ||
| 27 | expiresAt time.Time | 27 | expiresAt time.Time | ||
| 28 | + | label string | |||
| 28 | } | 29 | } | ||
| 29 | 30 | ||||
| 30 | // Anonymous returns the principal of a caller without credentials. | 31 | // Anonymous returns the principal of a caller without credentials. | ||
| @@ -41,6 +42,7 @@ func TokenPrincipal(t Token) Principal { | |||||
| 41 | scopes: slices.Clone(t.Scopes), | 42 | scopes: slices.Clone(t.Scopes), | ||
| 42 | repos: slices.Clone(t.Repos), | 43 | repos: slices.Clone(t.Repos), | ||
| 43 | expiresAt: t.ExpiresAt, | 44 | expiresAt: t.ExpiresAt, | ||
| 45 | + | label: t.Label, | |||
| 44 | } | 46 | } | ||
| 45 | } | 47 | } | ||
| 46 | 48 | ||||
| @@ -98,3 +100,7 @@ func (p Principal) Allows(s Scope, repo RepoName) bool { | |||||
| 98 | } | 100 | } | ||
| 99 | return true | 101 | return true | ||
| 100 | } | 102 | } | ||
| 103 | + | ||||
| 104 | + | // TokenLabel returns the label of the stored token, or "" for other | |||
| 105 | + | // principals. | |||
| 106 | + | func (p Principal) TokenLabel() string { return p.label } | |||
M internal/domain/repo.go +3 -0
| @@ -38,4 +38,7 @@ type Repo struct { | |||
| 38 | 38 | Visibility Visibility | |
| 39 | 39 | CreatedAt time.Time | |
| 40 | 40 | PushedAt time.Time // zero until the first push | |
| 41 | + | // Settings are the merge settings; the stores always fill them in, with | |
| 42 | + | // the defaults for a repository that never stored any. | |
| 43 | + | Settings RepoSettings | |
| 41 | 44 | } | |
| @@ -38,4 +38,7 @@ type Repo struct { | |||||
| 38 | Visibility Visibility | 38 | Visibility Visibility | ||
| 39 | CreatedAt time.Time | 39 | CreatedAt time.Time | ||
| 40 | PushedAt time.Time // zero until the first push | 40 | PushedAt time.Time // zero until the first push | ||
| 41 | + | // Settings are the merge settings; the stores always fill them in, with | |||
| 42 | + | // the defaults for a repository that never stored any. | |||
| 43 | + | Settings RepoSettings | |||
| 41 | } | 44 | } | ||
A internal/domain/signing.go +26 -0
| @@ -0,0 +1,26 @@ | |||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | ||
| 3 | + | package domain | |
| 4 | + | ||
| 5 | + | // SigningPublicKey is the public half of the key with which ermite signs the | |
| 6 | + | // commits it creates (ADR 0022). | |
| 7 | + | type SigningPublicKey struct { | |
| 8 | + | // AuthorizedKey is the key as one OpenSSH authorized_keys line, for | |
| 9 | + | // example "ssh-ed25519 AAAA...". | |
| 10 | + | AuthorizedKey string | |
| 11 | + | // Fingerprint is the SHA-256 fingerprint in the form ssh-keygen prints, | |
| 12 | + | // "SHA256:<base64>". | |
| 13 | + | Fingerprint string | |
| 14 | + | } | |
| 15 | + | ||
| 16 | + | // SigningKeyInfo is what a verifier needs to check the commits of ermite. | |
| 17 | + | type SigningKeyInfo struct { | |
| 18 | + | Key SigningPublicKey | |
| 19 | + | Committer Identity | |
| 20 | + | } | |
| 21 | + | ||
| 22 | + | // AllowedSigners returns the line of an allowed signers file for the key: | |
| 23 | + | // "<committer email> <authorized key>". | |
| 24 | + | func (i SigningKeyInfo) AllowedSigners() string { | |
| 25 | + | return i.Committer.Email + " " + i.Key.AuthorizedKey | |
| 26 | + | } | |
| @@ -0,0 +1,26 @@ | |||||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |||
| 2 | + | ||||
| 3 | + | package domain | |||
| 4 | + | ||||
| 5 | + | // SigningPublicKey is the public half of the key with which ermite signs the | |||
| 6 | + | // commits it creates (ADR 0022). | |||
| 7 | + | type SigningPublicKey struct { | |||
| 8 | + | // AuthorizedKey is the key as one OpenSSH authorized_keys line, for | |||
| 9 | + | // example "ssh-ed25519 AAAA...". | |||
| 10 | + | AuthorizedKey string | |||
| 11 | + | // Fingerprint is the SHA-256 fingerprint in the form ssh-keygen prints, | |||
| 12 | + | // "SHA256:<base64>". | |||
| 13 | + | Fingerprint string | |||
| 14 | + | } | |||
| 15 | + | ||||
| 16 | + | // SigningKeyInfo is what a verifier needs to check the commits of ermite. | |||
| 17 | + | type SigningKeyInfo struct { | |||
| 18 | + | Key SigningPublicKey | |||
| 19 | + | Committer Identity | |||
| 20 | + | } | |||
| 21 | + | ||||
| 22 | + | // AllowedSigners returns the line of an allowed signers file for the key: | |||
| 23 | + | // "<committer email> <authorized key>". | |||
| 24 | + | func (i SigningKeyInfo) AllowedSigners() string { | |||
| 25 | + | return i.Committer.Email + " " + i.Key.AuthorizedKey | |||
| 26 | + | } | |||
A internal/domain/text.go +176 -0
| @@ -0,0 +1,176 @@ | |||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | ||
| 3 | + | package domain | |
| 4 | + | ||
| 5 | + | import ( | |
| 6 | + | "errors" | |
| 7 | + | "fmt" | |
| 8 | + | "unicode" | |
| 9 | + | "unicode/utf8" | |
| 10 | + | ) | |
| 11 | + | ||
| 12 | + | // Limits of the plain text of a merge request (ADR 0017), in characters. | |
| 13 | + | const ( | |
| 14 | + | // MaxTitleLen is the longest merge request title. | |
| 15 | + | MaxTitleLen = 120 | |
| 16 | + | // MaxTextLen is the longest description or comment. | |
| 17 | + | MaxTextLen = 8000 | |
| 18 | + | ) | |
| 19 | + | ||
| 20 | + | // ErrInvalidText means a text breaks the rules of ParseMRTitle or | |
| 21 | + | // ParsePlainText. | |
| 22 | + | var ErrInvalidText = errors.New("invalid text") | |
| 23 | + | ||
| 24 | + | // TextError says why a text is invalid. It matches ErrInvalidText. | |
| 25 | + | type TextError struct { | |
| 26 | + | // Reason completes the sentence "the text ...", for example "is not valid | |
| 27 | + | // UTF-8". | |
| 28 | + | Reason string | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | // Error implements error. | |
| 32 | + | func (e *TextError) Error() string { return "text " + e.Reason } | |
| 33 | + | ||
| 34 | + | // Unwrap makes errors.Is(err, ErrInvalidText) true. | |
| 35 | + | func (e *TextError) Unwrap() error { return ErrInvalidText } | |
| 36 | + | ||
| 37 | + | // MRTitle is a validated merge request title: one line of plain text, at most | |
| 38 | + | // MaxTitleLen characters. Only ParseMRTitle builds a non-zero value. | |
| 39 | + | type MRTitle struct{ s string } | |
| 40 | + | ||
| 41 | + | // ParseMRTitle validates s: not blank, valid UTF-8, one line, at most | |
| 42 | + | // MaxTitleLen characters, no control character and no bidirectional override | |
| 43 | + | // or isolate character. | |
| 44 | + | func ParseMRTitle(s string) (MRTitle, error) { | |
| 45 | + | if reason := textProblem(s, false, MaxTitleLen); reason != "" { | |
| 46 | + | return MRTitle{}, &TextError{Reason: reason} | |
| 47 | + | } | |
| 48 | + | if isBlank(s) { | |
| 49 | + | return MRTitle{}, &TextError{Reason: "is empty"} | |
| 50 | + | } | |
| 51 | + | return MRTitle{s: s}, nil | |
| 52 | + | } | |
| 53 | + | ||
| 54 | + | // String returns the title. | |
| 55 | + | func (t MRTitle) String() string { return t.s } | |
| 56 | + | ||
| 57 | + | // IsZero reports whether t is the zero value. | |
| 58 | + | func (t MRTitle) IsZero() bool { return t.s == "" } | |
| 59 | + | ||
| 60 | + | // PlainText is a validated multi-line plain text, such as a description or a | |
| 61 | + | // comment: valid UTF-8, at most MaxTextLen characters, line breaks allowed, | |
| 62 | + | // no other control character, no bidirectional override or isolate character. | |
| 63 | + | // The zero value is the empty text. | |
| 64 | + | type PlainText struct{ s string } | |
| 65 | + | ||
| 66 | + | // ParsePlainText validates s. The empty text is valid: a caller that needs | |
| 67 | + | // text checks IsBlank. | |
| 68 | + | func ParsePlainText(s string) (PlainText, error) { | |
| 69 | + | if reason := textProblem(s, true, MaxTextLen); reason != "" { | |
| 70 | + | return PlainText{}, &TextError{Reason: reason} | |
| 71 | + | } | |
| 72 | + | return PlainText{s: s}, nil | |
| 73 | + | } | |
| 74 | + | ||
| 75 | + | // String returns the text. | |
| 76 | + | func (t PlainText) String() string { return t.s } | |
| 77 | + | ||
| 78 | + | // IsBlank reports whether the text is empty or only white space. | |
| 79 | + | func (t PlainText) IsBlank() bool { return isBlank(t.s) } | |
| 80 | + | ||
| 81 | + | func isBlank(s string) bool { | |
| 82 | + | for _, r := range s { | |
| 83 | + | if !unicode.IsSpace(r) { | |
| 84 | + | return false | |
| 85 | + | } | |
| 86 | + | } | |
| 87 | + | return true | |
| 88 | + | } | |
| 89 | + | ||
| 90 | + | // textProblem returns why s breaks the rules, or "". | |
| 91 | + | func textProblem(s string, multiline bool, maxLen int) string { | |
| 92 | + | if !utf8.ValidString(s) { | |
| 93 | + | return "is not valid UTF-8" | |
| 94 | + | } | |
| 95 | + | if n := utf8.RuneCountInString(s); n > maxLen { | |
| 96 | + | return fmt.Sprintf("is longer than %d characters (%d)", maxLen, n) | |
| 97 | + | } | |
| 98 | + | for _, r := range s { | |
| 99 | + | switch { | |
| 100 | + | case r == '\n': | |
| 101 | + | if !multiline { | |
| 102 | + | return "must be one line" | |
| 103 | + | } | |
| 104 | + | case unicode.IsControl(r): | |
| 105 | + | return fmt.Sprintf("contains the control character U+%04X", r) | |
| 106 | + | case isBidiControl(r): | |
| 107 | + | return fmt.Sprintf("contains the bidirectional override or isolate character U+%04X", r) | |
| 108 | + | } | |
| 109 | + | } | |
| 110 | + | return "" | |
| 111 | + | } | |
| 112 | + | ||
| 113 | + | // isBidiControl reports whether r is a bidirectional embedding, override or | |
| 114 | + | // isolate character (U+202A to U+202E, U+2066 to U+2069), which can make text | |
| 115 | + | // read differently from how it is stored. | |
| 116 | + | func isBidiControl(r rune) bool { | |
| 117 | + | return (r >= 0x202a && r <= 0x202e) || (r >= 0x2066 && r <= 0x2069) | |
| 118 | + | } | |
| 119 | + | ||
| 120 | + | // FieldViolation names a request field that breaks a rule. | |
| 121 | + | type FieldViolation struct { | |
| 122 | + | // Field is the name of the field in the API contract, for example | |
| 123 | + | // "title". | |
| 124 | + | Field string | |
| 125 | + | // Reason says what is wrong with it. | |
| 126 | + | Reason string | |
| 127 | + | } | |
| 128 | + | ||
| 129 | + | // InvalidFieldsError is a request rejected whole because of its fields. It | |
| 130 | + | // matches ErrInvalid. | |
| 131 | + | type InvalidFieldsError struct { | |
| 132 | + | Fields []FieldViolation | |
| 133 | + | } | |
| 134 | + | ||
| 135 | + | // Error implements error. | |
| 136 | + | func (e *InvalidFieldsError) Error() string { | |
| 137 | + | msg := "invalid argument" | |
| 138 | + | for i, f := range e.Fields { | |
| 139 | + | sep := "; " | |
| 140 | + | if i == 0 { | |
| 141 | + | sep = ": " | |
| 142 | + | } | |
| 143 | + | msg += sep + f.Field + " " + f.Reason | |
| 144 | + | } | |
| 145 | + | return msg | |
| 146 | + | } | |
| 147 | + | ||
| 148 | + | // Unwrap makes errors.Is(err, ErrInvalid) true. | |
| 149 | + | func (e *InvalidFieldsError) Unwrap() error { return ErrInvalid } | |
| 150 | + | ||
| 151 | + | // Violations collects the violations of a request. | |
| 152 | + | type Violations struct{ list []FieldViolation } | |
| 153 | + | ||
| 154 | + | // Add records that field breaks a rule. | |
| 155 | + | func (v *Violations) Add(field, reason string) { | |
| 156 | + | v.list = append(v.list, FieldViolation{Field: field, Reason: reason}) | |
| 157 | + | } | |
| 158 | + | ||
| 159 | + | // AddErr records an error of a parser for field: the reason of a *TextError, | |
| 160 | + | // else the text of the error. | |
| 161 | + | func (v *Violations) AddErr(field string, err error) { | |
| 162 | + | var te *TextError | |
| 163 | + | if errors.As(err, &te) { | |
| 164 | + | v.Add(field, te.Reason) | |
| 165 | + | return | |
| 166 | + | } | |
| 167 | + | v.Add(field, err.Error()) | |
| 168 | + | } | |
| 169 | + | ||
| 170 | + | // Err returns nil without violations, else an *InvalidFieldsError. | |
| 171 | + | func (v *Violations) Err() error { | |
| 172 | + | if len(v.list) == 0 { | |
| 173 | + | return nil | |
| 174 | + | } | |
| 175 | + | return &InvalidFieldsError{Fields: append([]FieldViolation(nil), v.list...)} | |
| 176 | + | } | |
| @@ -0,0 +1,176 @@ | |||||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |||
| 2 | + | ||||
| 3 | + | package domain | |||
| 4 | + | ||||
| 5 | + | import ( | |||
| 6 | + | "errors" | |||
| 7 | + | "fmt" | |||
| 8 | + | "unicode" | |||
| 9 | + | "unicode/utf8" | |||
| 10 | + | ) | |||
| 11 | + | ||||
| 12 | + | // Limits of the plain text of a merge request (ADR 0017), in characters. | |||
| 13 | + | const ( | |||
| 14 | + | // MaxTitleLen is the longest merge request title. | |||
| 15 | + | MaxTitleLen = 120 | |||
| 16 | + | // MaxTextLen is the longest description or comment. | |||
| 17 | + | MaxTextLen = 8000 | |||
| 18 | + | ) | |||
| 19 | + | ||||
| 20 | + | // ErrInvalidText means a text breaks the rules of ParseMRTitle or | |||
| 21 | + | // ParsePlainText. | |||
| 22 | + | var ErrInvalidText = errors.New("invalid text") | |||
| 23 | + | ||||
| 24 | + | // TextError says why a text is invalid. It matches ErrInvalidText. | |||
| 25 | + | type TextError struct { | |||
| 26 | + | // Reason completes the sentence "the text ...", for example "is not valid | |||
| 27 | + | // UTF-8". | |||
| 28 | + | Reason string | |||
| 29 | + | } | |||
| 30 | + | ||||
| 31 | + | // Error implements error. | |||
| 32 | + | func (e *TextError) Error() string { return "text " + e.Reason } | |||
| 33 | + | ||||
| 34 | + | // Unwrap makes errors.Is(err, ErrInvalidText) true. | |||
| 35 | + | func (e *TextError) Unwrap() error { return ErrInvalidText } | |||
| 36 | + | ||||
| 37 | + | // MRTitle is a validated merge request title: one line of plain text, at most | |||
| 38 | + | // MaxTitleLen characters. Only ParseMRTitle builds a non-zero value. | |||
| 39 | + | type MRTitle struct{ s string } | |||
| 40 | + | ||||
| 41 | + | // ParseMRTitle validates s: not blank, valid UTF-8, one line, at most | |||
| 42 | + | // MaxTitleLen characters, no control character and no bidirectional override | |||
| 43 | + | // or isolate character. | |||
| 44 | + | func ParseMRTitle(s string) (MRTitle, error) { | |||
| 45 | + | if reason := textProblem(s, false, MaxTitleLen); reason != "" { | |||
| 46 | + | return MRTitle{}, &TextError{Reason: reason} | |||
| 47 | + | } | |||
| 48 | + | if isBlank(s) { | |||
| 49 | + | return MRTitle{}, &TextError{Reason: "is empty"} | |||
| 50 | + | } | |||
| 51 | + | return MRTitle{s: s}, nil | |||
| 52 | + | } | |||
| 53 | + | ||||
| 54 | + | // String returns the title. | |||
| 55 | + | func (t MRTitle) String() string { return t.s } | |||
| 56 | + | ||||
| 57 | + | // IsZero reports whether t is the zero value. | |||
| 58 | + | func (t MRTitle) IsZero() bool { return t.s == "" } | |||
| 59 | + | ||||
| 60 | + | // PlainText is a validated multi-line plain text, such as a description or a | |||
| 61 | + | // comment: valid UTF-8, at most MaxTextLen characters, line breaks allowed, | |||
| 62 | + | // no other control character, no bidirectional override or isolate character. | |||
| 63 | + | // The zero value is the empty text. | |||
| 64 | + | type PlainText struct{ s string } | |||
| 65 | + | ||||
| 66 | + | // ParsePlainText validates s. The empty text is valid: a caller that needs | |||
| 67 | + | // text checks IsBlank. | |||
| 68 | + | func ParsePlainText(s string) (PlainText, error) { | |||
| 69 | + | if reason := textProblem(s, true, MaxTextLen); reason != "" { | |||
| 70 | + | return PlainText{}, &TextError{Reason: reason} | |||
| 71 | + | } | |||
| 72 | + | return PlainText{s: s}, nil | |||
| 73 | + | } | |||
| 74 | + | ||||
| 75 | + | // String returns the text. | |||
| 76 | + | func (t PlainText) String() string { return t.s } | |||
| 77 | + | ||||
| 78 | + | // IsBlank reports whether the text is empty or only white space. | |||
| 79 | + | func (t PlainText) IsBlank() bool { return isBlank(t.s) } | |||
| 80 | + | ||||
| 81 | + | func isBlank(s string) bool { | |||
| 82 | + | for _, r := range s { | |||
| 83 | + | if !unicode.IsSpace(r) { | |||
| 84 | + | return false | |||
| 85 | + | } | |||
| 86 | + | } | |||
| 87 | + | return true | |||
| 88 | + | } | |||
| 89 | + | ||||
| 90 | + | // textProblem returns why s breaks the rules, or "". | |||
| 91 | + | func textProblem(s string, multiline bool, maxLen int) string { | |||
| 92 | + | if !utf8.ValidString(s) { | |||
| 93 | + | return "is not valid UTF-8" | |||
| 94 | + | } | |||
| 95 | + | if n := utf8.RuneCountInString(s); n > maxLen { | |||
| 96 | + | return fmt.Sprintf("is longer than %d characters (%d)", maxLen, n) | |||
| 97 | + | } | |||
| 98 | + | for _, r := range s { | |||
| 99 | + | switch { | |||
| 100 | + | case r == '\n': | |||
| 101 | + | if !multiline { | |||
| 102 | + | return "must be one line" | |||
| 103 | + | } | |||
| 104 | + | case unicode.IsControl(r): | |||
| 105 | + | return fmt.Sprintf("contains the control character U+%04X", r) | |||
| 106 | + | case isBidiControl(r): | |||
| 107 | + | return fmt.Sprintf("contains the bidirectional override or isolate character U+%04X", r) | |||
| 108 | + | } | |||
| 109 | + | } | |||
| 110 | + | return "" | |||
| 111 | + | } | |||
| 112 | + | ||||
| 113 | + | // isBidiControl reports whether r is a bidirectional embedding, override or | |||
| 114 | + | // isolate character (U+202A to U+202E, U+2066 to U+2069), which can make text | |||
| 115 | + | // read differently from how it is stored. | |||
| 116 | + | func isBidiControl(r rune) bool { | |||
| 117 | + | return (r >= 0x202a && r <= 0x202e) || (r >= 0x2066 && r <= 0x2069) | |||
| 118 | + | } | |||
| 119 | + | ||||
| 120 | + | // FieldViolation names a request field that breaks a rule. | |||
| 121 | + | type FieldViolation struct { | |||
| 122 | + | // Field is the name of the field in the API contract, for example | |||
| 123 | + | // "title". | |||
| 124 | + | Field string | |||
| 125 | + | // Reason says what is wrong with it. | |||
| 126 | + | Reason string | |||
| 127 | + | } | |||
| 128 | + | ||||
| 129 | + | // InvalidFieldsError is a request rejected whole because of its fields. It | |||
| 130 | + | // matches ErrInvalid. | |||
| 131 | + | type InvalidFieldsError struct { | |||
| 132 | + | Fields []FieldViolation | |||
| 133 | + | } | |||
| 134 | + | ||||
| 135 | + | // Error implements error. | |||
| 136 | + | func (e *InvalidFieldsError) Error() string { | |||
| 137 | + | msg := "invalid argument" | |||
| 138 | + | for i, f := range e.Fields { | |||
| 139 | + | sep := "; " | |||
| 140 | + | if i == 0 { | |||
| 141 | + | sep = ": " | |||
| 142 | + | } | |||
| 143 | + | msg += sep + f.Field + " " + f.Reason | |||
| 144 | + | } | |||
| 145 | + | return msg | |||
| 146 | + | } | |||
| 147 | + | ||||
| 148 | + | // Unwrap makes errors.Is(err, ErrInvalid) true. | |||
| 149 | + | func (e *InvalidFieldsError) Unwrap() error { return ErrInvalid } | |||
| 150 | + | ||||
| 151 | + | // Violations collects the violations of a request. | |||
| 152 | + | type Violations struct{ list []FieldViolation } | |||
| 153 | + | ||||
| 154 | + | // Add records that field breaks a rule. | |||
| 155 | + | func (v *Violations) Add(field, reason string) { | |||
| 156 | + | v.list = append(v.list, FieldViolation{Field: field, Reason: reason}) | |||
| 157 | + | } | |||
| 158 | + | ||||
| 159 | + | // AddErr records an error of a parser for field: the reason of a *TextError, | |||
| 160 | + | // else the text of the error. | |||
| 161 | + | func (v *Violations) AddErr(field string, err error) { | |||
| 162 | + | var te *TextError | |||
| 163 | + | if errors.As(err, &te) { | |||
| 164 | + | v.Add(field, te.Reason) | |||
| 165 | + | return | |||
| 166 | + | } | |||
| 167 | + | v.Add(field, err.Error()) | |||
| 168 | + | } | |||
| 169 | + | ||||
| 170 | + | // Err returns nil without violations, else an *InvalidFieldsError. | |||
| 171 | + | func (v *Violations) Err() error { | |||
| 172 | + | if len(v.list) == 0 { | |||
| 173 | + | return nil | |||
| 174 | + | } | |||
| 175 | + | return &InvalidFieldsError{Fields: append([]FieldViolation(nil), v.list...)} | |||
| 176 | + | } | |||
A internal/domain/text_test.go +126 -0
| @@ -0,0 +1,126 @@ | |||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | ||
| 3 | + | package domain_test | |
| 4 | + | ||
| 5 | + | import ( | |
| 6 | + | "errors" | |
| 7 | + | "strings" | |
| 8 | + | "testing" | |
| 9 | + | ||
| 10 | + | "github.com/stretchr/testify/assert" | |
| 11 | + | "github.com/stretchr/testify/require" | |
| 12 | + | ||
| 13 | + | "bnema.dev/ermite/internal/domain" | |
| 14 | + | ) | |
| 15 | + | ||
| 16 | + | // textCases are the rules shared by every text; multi says whether line breaks | |
| 17 | + | // are allowed. | |
| 18 | + | func TestParseMRTitle(t *testing.T) { | |
| 19 | + | tests := []struct { | |
| 20 | + | name string | |
| 21 | + | in string | |
| 22 | + | reason string // "" when valid | |
| 23 | + | }{ | |
| 24 | + | {"plain", "Add the login page", ""}, | |
| 25 | + | {"unicode", "Ajoute la page d’accueil — ça marche 🎉", ""}, | |
| 26 | + | {"max length in characters", strings.Repeat("é", domain.MaxTitleLen), ""}, | |
| 27 | + | {"too long", strings.Repeat("a", domain.MaxTitleLen+1), "longer than 120"}, | |
| 28 | + | {"empty", "", "is empty"}, | |
| 29 | + | {"blank", " \u00a0 ", "is empty"}, | |
| 30 | + | {"line break", "two\nlines", "one line"}, | |
| 31 | + | {"carriage return", "a\rb", "control character U+000D"}, | |
| 32 | + | {"tab", "a\tb", "control character U+0009"}, | |
| 33 | + | {"nul", "a\x00b", "control character U+0000"}, | |
| 34 | + | {"escape", "\x1b[31mred", "control character U+001B"}, | |
| 35 | + | {"delete", "a\x7fb", "control character U+007F"}, | |
| 36 | + | {"c1 control", "a\u0085b", "control character U+0085"}, | |
| 37 | + | {"invalid utf-8", "a\xffb", "not valid UTF-8"}, | |
| 38 | + | {"bidi override", "evil \u202e txt.exe", "U+202E"}, | |
| 39 | + | {"bidi embedding", "a\u202ab", "U+202A"}, | |
| 40 | + | {"bidi isolate", "a\u2066b", "U+2066"}, | |
| 41 | + | {"bidi pop isolate", "a\u2069b", "U+2069"}, | |
| 42 | + | {"zero width joiner is plain text", "a\u200db", ""}, | |
| 43 | + | } | |
| 44 | + | for _, tc := range tests { | |
| 45 | + | t.Run(tc.name, func(t *testing.T) { | |
| 46 | + | got, err := domain.ParseMRTitle(tc.in) | |
| 47 | + | if tc.reason == "" { | |
| 48 | + | require.NoError(t, err) | |
| 49 | + | assert.Equal(t, tc.in, got.String()) | |
| 50 | + | assert.False(t, got.IsZero()) | |
| 51 | + | return | |
| 52 | + | } | |
| 53 | + | require.Error(t, err) | |
| 54 | + | assert.ErrorIs(t, err, domain.ErrInvalidText) | |
| 55 | + | assert.Contains(t, err.Error(), tc.reason) | |
| 56 | + | assert.True(t, got.IsZero()) | |
| 57 | + | }) | |
| 58 | + | } | |
| 59 | + | } | |
| 60 | + | ||
| 61 | + | func TestParsePlainText(t *testing.T) { | |
| 62 | + | tests := []struct { | |
| 63 | + | name string | |
| 64 | + | in string | |
| 65 | + | reason string | |
| 66 | + | }{ | |
| 67 | + | {"empty is valid", "", ""}, | |
| 68 | + | {"multi-line", "one\ntwo\n\nthree", ""}, | |
| 69 | + | {"trailing newline", "x\n", ""}, | |
| 70 | + | {"max length", strings.Repeat("a\n", domain.MaxTextLen/2), ""}, | |
| 71 | + | {"max length in characters not bytes", strings.Repeat("€", domain.MaxTextLen), ""}, | |
| 72 | + | {"too long", strings.Repeat("a", domain.MaxTextLen+1), "longer than 8000"}, | |
| 73 | + | {"carriage return", "a\r\nb", "control character U+000D"}, | |
| 74 | + | {"tab", "a\tb", "control character U+0009"}, | |
| 75 | + | {"escape sequence", "a\x1b]0;title\x07", "control character U+001B"}, | |
| 76 | + | {"bell", "a\x07", "control character U+0007"}, | |
| 77 | + | {"invalid utf-8", "ok\n\xc3\x28", "not valid UTF-8"}, | |
| 78 | + | {"bidi override", "x\u202ey", "U+202E"}, | |
| 79 | + | {"bidi left-to-right isolate", "x\u2066y", "U+2066"}, | |
| 80 | + | {"bidi first strong isolate", "x\u2068y", "U+2068"}, | |
| 81 | + | {"left-to-right mark is plain text", "x\u200ey", ""}, | |
| 82 | + | } | |
| 83 | + | for _, tc := range tests { | |
| 84 | + | t.Run(tc.name, func(t *testing.T) { | |
| 85 | + | got, err := domain.ParsePlainText(tc.in) | |
| 86 | + | if tc.reason == "" { | |
| 87 | + | require.NoError(t, err) | |
| 88 | + | assert.Equal(t, tc.in, got.String()) | |
| 89 | + | return | |
| 90 | + | } | |
| 91 | + | require.Error(t, err) | |
| 92 | + | assert.ErrorIs(t, err, domain.ErrInvalidText) | |
| 93 | + | assert.Contains(t, err.Error(), tc.reason) | |
| 94 | + | assert.Empty(t, got.String()) | |
| 95 | + | }) | |
| 96 | + | } | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | func TestPlainTextIsBlank(t *testing.T) { | |
| 100 | + | for in, blank := range map[string]bool{"": true, " \n\n ": true, "x": false, " x ": false} { | |
| 101 | + | got, err := domain.ParsePlainText(in) | |
| 102 | + | require.NoError(t, err) | |
| 103 | + | assert.Equal(t, blank, got.IsBlank(), "%q", in) | |
| 104 | + | } | |
| 105 | + | } | |
| 106 | + | ||
| 107 | + | func TestViolations(t *testing.T) { | |
| 108 | + | var v domain.Violations | |
| 109 | + | require.NoError(t, v.Err()) | |
| 110 | + | ||
| 111 | + | _, err := domain.ParseMRTitle("a\nb") | |
| 112 | + | v.AddErr("title", err) | |
| 113 | + | v.Add("source_branch", "does not exist") | |
| 114 | + | v.AddErr("text", errors.New("boom")) | |
| 115 | + | ||
| 116 | + | err = v.Err() | |
| 117 | + | require.ErrorIs(t, err, domain.ErrInvalid) | |
| 118 | + | var fe *domain.InvalidFieldsError | |
| 119 | + | require.ErrorAs(t, err, &fe) | |
| 120 | + | assert.Equal(t, []domain.FieldViolation{ | |
| 121 | + | {Field: "title", Reason: "must be one line"}, | |
| 122 | + | {Field: "source_branch", Reason: "does not exist"}, | |
| 123 | + | {Field: "text", Reason: "boom"}, | |
| 124 | + | }, fe.Fields) | |
| 125 | + | assert.Equal(t, "invalid argument: title must be one line; source_branch does not exist; text boom", err.Error()) | |
| 126 | + | } | |
| @@ -0,0 +1,126 @@ | |||||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |||
| 2 | + | ||||
| 3 | + | package domain_test | |||
| 4 | + | ||||
| 5 | + | import ( | |||
| 6 | + | "errors" | |||
| 7 | + | "strings" | |||
| 8 | + | "testing" | |||
| 9 | + | ||||
| 10 | + | "github.com/stretchr/testify/assert" | |||
| 11 | + | "github.com/stretchr/testify/require" | |||
| 12 | + | ||||
| 13 | + | "bnema.dev/ermite/internal/domain" | |||
| 14 | + | ) | |||
| 15 | + | ||||
| 16 | + | // textCases are the rules shared by every text; multi says whether line breaks | |||
| 17 | + | // are allowed. | |||
| 18 | + | func TestParseMRTitle(t *testing.T) { | |||
| 19 | + | tests := []struct { | |||
| 20 | + | name string | |||
| 21 | + | in string | |||
| 22 | + | reason string // "" when valid | |||
| 23 | + | }{ | |||
| 24 | + | {"plain", "Add the login page", ""}, | |||
| 25 | + | {"unicode", "Ajoute la page d’accueil — ça marche 🎉", ""}, | |||
| 26 | + | {"max length in characters", strings.Repeat("é", domain.MaxTitleLen), ""}, | |||
| 27 | + | {"too long", strings.Repeat("a", domain.MaxTitleLen+1), "longer than 120"}, | |||
| 28 | + | {"empty", "", "is empty"}, | |||
| 29 | + | {"blank", " \u00a0 ", "is empty"}, | |||
| 30 | + | {"line break", "two\nlines", "one line"}, | |||
| 31 | + | {"carriage return", "a\rb", "control character U+000D"}, | |||
| 32 | + | {"tab", "a\tb", "control character U+0009"}, | |||
| 33 | + | {"nul", "a\x00b", "control character U+0000"}, | |||
| 34 | + | {"escape", "\x1b[31mred", "control character U+001B"}, | |||
| 35 | + | {"delete", "a\x7fb", "control character U+007F"}, | |||
| 36 | + | {"c1 control", "a\u0085b", "control character U+0085"}, | |||
| 37 | + | {"invalid utf-8", "a\xffb", "not valid UTF-8"}, | |||
| 38 | + | {"bidi override", "evil \u202e txt.exe", "U+202E"}, | |||
| 39 | + | {"bidi embedding", "a\u202ab", "U+202A"}, | |||
| 40 | + | {"bidi isolate", "a\u2066b", "U+2066"}, | |||
| 41 | + | {"bidi pop isolate", "a\u2069b", "U+2069"}, | |||
| 42 | + | {"zero width joiner is plain text", "a\u200db", ""}, | |||
| 43 | + | } | |||
| 44 | + | for _, tc := range tests { | |||
| 45 | + | t.Run(tc.name, func(t *testing.T) { | |||
| 46 | + | got, err := domain.ParseMRTitle(tc.in) | |||
| 47 | + | if tc.reason == "" { | |||
| 48 | + | require.NoError(t, err) | |||
| 49 | + | assert.Equal(t, tc.in, got.String()) | |||
| 50 | + | assert.False(t, got.IsZero()) | |||
| 51 | + | return | |||
| 52 | + | } | |||
| 53 | + | require.Error(t, err) | |||
| 54 | + | assert.ErrorIs(t, err, domain.ErrInvalidText) | |||
| 55 | + | assert.Contains(t, err.Error(), tc.reason) | |||
| 56 | + | assert.True(t, got.IsZero()) | |||
| 57 | + | }) | |||
| 58 | + | } | |||
| 59 | + | } | |||
| 60 | + | ||||
| 61 | + | func TestParsePlainText(t *testing.T) { | |||
| 62 | + | tests := []struct { | |||
| 63 | + | name string | |||
| 64 | + | in string | |||
| 65 | + | reason string | |||
| 66 | + | }{ | |||
| 67 | + | {"empty is valid", "", ""}, | |||
| 68 | + | {"multi-line", "one\ntwo\n\nthree", ""}, | |||
| 69 | + | {"trailing newline", "x\n", ""}, | |||
| 70 | + | {"max length", strings.Repeat("a\n", domain.MaxTextLen/2), ""}, | |||
| 71 | + | {"max length in characters not bytes", strings.Repeat("€", domain.MaxTextLen), ""}, | |||
| 72 | + | {"too long", strings.Repeat("a", domain.MaxTextLen+1), "longer than 8000"}, | |||
| 73 | + | {"carriage return", "a\r\nb", "control character U+000D"}, | |||
| 74 | + | {"tab", "a\tb", "control character U+0009"}, | |||
| 75 | + | {"escape sequence", "a\x1b]0;title\x07", "control character U+001B"}, | |||
| 76 | + | {"bell", "a\x07", "control character U+0007"}, | |||
| 77 | + | {"invalid utf-8", "ok\n\xc3\x28", "not valid UTF-8"}, | |||
| 78 | + | {"bidi override", "x\u202ey", "U+202E"}, | |||
| 79 | + | {"bidi left-to-right isolate", "x\u2066y", "U+2066"}, | |||
| 80 | + | {"bidi first strong isolate", "x\u2068y", "U+2068"}, | |||
| 81 | + | {"left-to-right mark is plain text", "x\u200ey", ""}, | |||
| 82 | + | } | |||
| 83 | + | for _, tc := range tests { | |||
| 84 | + | t.Run(tc.name, func(t *testing.T) { | |||
| 85 | + | got, err := domain.ParsePlainText(tc.in) | |||
| 86 | + | if tc.reason == "" { | |||
| 87 | + | require.NoError(t, err) | |||
| 88 | + | assert.Equal(t, tc.in, got.String()) | |||
| 89 | + | return | |||
| 90 | + | } | |||
| 91 | + | require.Error(t, err) | |||
| 92 | + | assert.ErrorIs(t, err, domain.ErrInvalidText) | |||
| 93 | + | assert.Contains(t, err.Error(), tc.reason) | |||
| 94 | + | assert.Empty(t, got.String()) | |||
| 95 | + | }) | |||
| 96 | + | } | |||
| 97 | + | } | |||
| 98 | + | ||||
| 99 | + | func TestPlainTextIsBlank(t *testing.T) { | |||
| 100 | + | for in, blank := range map[string]bool{"": true, " \n\n ": true, "x": false, " x ": false} { | |||
| 101 | + | got, err := domain.ParsePlainText(in) | |||
| 102 | + | require.NoError(t, err) | |||
| 103 | + | assert.Equal(t, blank, got.IsBlank(), "%q", in) | |||
| 104 | + | } | |||
| 105 | + | } | |||
| 106 | + | ||||
| 107 | + | func TestViolations(t *testing.T) { | |||
| 108 | + | var v domain.Violations | |||
| 109 | + | require.NoError(t, v.Err()) | |||
| 110 | + | ||||
| 111 | + | _, err := domain.ParseMRTitle("a\nb") | |||
| 112 | + | v.AddErr("title", err) | |||
| 113 | + | v.Add("source_branch", "does not exist") | |||
| 114 | + | v.AddErr("text", errors.New("boom")) | |||
| 115 | + | ||||
| 116 | + | err = v.Err() | |||
| 117 | + | require.ErrorIs(t, err, domain.ErrInvalid) | |||
| 118 | + | var fe *domain.InvalidFieldsError | |||
| 119 | + | require.ErrorAs(t, err, &fe) | |||
| 120 | + | assert.Equal(t, []domain.FieldViolation{ | |||
| 121 | + | {Field: "title", Reason: "must be one line"}, | |||
| 122 | + | {Field: "source_branch", Reason: "does not exist"}, | |||
| 123 | + | {Field: "text", Reason: "boom"}, | |||
| 124 | + | }, fe.Fields) | |||
| 125 | + | assert.Equal(t, "invalid argument: title must be one line; source_branch does not exist; text boom", err.Error()) | |||
| 126 | + | } | |||
A internal/ports/mocks/CommitSigner.go +150 -0
| @@ -0,0 +1,150 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "bnema.dev/ermite/internal/domain" | |
| 9 | + | mock "github.com/stretchr/testify/mock" | |
| 10 | + | ) | |
| 11 | + | ||
| 12 | + | // NewCommitSigner creates a new instance of CommitSigner. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 13 | + | // The first argument is typically a *testing.T value. | |
| 14 | + | func NewCommitSigner(t interface { | |
| 15 | + | mock.TestingT | |
| 16 | + | Cleanup(func()) | |
| 17 | + | }) *CommitSigner { | |
| 18 | + | if helper, ok := t.(interface{ Helper() }); ok { | |
| 19 | + | helper.Helper() | |
| 20 | + | } | |
| 21 | + | ||
| 22 | + | mock := &CommitSigner{} | |
| 23 | + | mock.Mock.Test(t) | |
| 24 | + | ||
| 25 | + | t.Cleanup(func() { | |
| 26 | + | if helper, ok := t.(interface{ Helper() }); ok { | |
| 27 | + | helper.Helper() | |
| 28 | + | } | |
| 29 | + | mock.AssertExpectations(t) | |
| 30 | + | }) | |
| 31 | + | ||
| 32 | + | return mock | |
| 33 | + | } | |
| 34 | + | ||
| 35 | + | // CommitSigner is an autogenerated mock type for the CommitSigner type | |
| 36 | + | type CommitSigner struct { | |
| 37 | + | mock.Mock | |
| 38 | + | } | |
| 39 | + | ||
| 40 | + | type CommitSigner_Expecter struct { | |
| 41 | + | mock *mock.Mock | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | func (_m *CommitSigner) EXPECT() *CommitSigner_Expecter { | |
| 45 | + | return &CommitSigner_Expecter{mock: &_m.Mock} | |
| 46 | + | } | |
| 47 | + | ||
| 48 | + | // PublicKey provides a mock function for the type CommitSigner | |
| 49 | + | func (_mock *CommitSigner) PublicKey() domain.SigningPublicKey { | |
| 50 | + | ret := _mock.Called() | |
| 51 | + | ||
| 52 | + | if len(ret) == 0 { | |
| 53 | + | panic("no return value specified for PublicKey") | |
| 54 | + | } | |
| 55 | + | ||
| 56 | + | var r0 domain.SigningPublicKey | |
| 57 | + | if returnFunc, ok := ret.Get(0).(func() domain.SigningPublicKey); ok { | |
| 58 | + | r0 = returnFunc() | |
| 59 | + | } else { | |
| 60 | + | r0 = ret.Get(0).(domain.SigningPublicKey) | |
| 61 | + | } | |
| 62 | + | return r0 | |
| 63 | + | } | |
| 64 | + | ||
| 65 | + | // CommitSigner_PublicKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PublicKey' | |
| 66 | + | type CommitSigner_PublicKey_Call struct { | |
| 67 | + | *mock.Call | |
| 68 | + | } | |
| 69 | + | ||
| 70 | + | // PublicKey is a helper method to define mock.On call | |
| 71 | + | func (_e *CommitSigner_Expecter) PublicKey() *CommitSigner_PublicKey_Call { | |
| 72 | + | return &CommitSigner_PublicKey_Call{Call: _e.mock.On("PublicKey")} | |
| 73 | + | } | |
| 74 | + | ||
| 75 | + | func (_c *CommitSigner_PublicKey_Call) Run(run func()) *CommitSigner_PublicKey_Call { | |
| 76 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 77 | + | run() | |
| 78 | + | }) | |
| 79 | + | return _c | |
| 80 | + | } | |
| 81 | + | ||
| 82 | + | func (_c *CommitSigner_PublicKey_Call) Return(signingPublicKey domain.SigningPublicKey) *CommitSigner_PublicKey_Call { | |
| 83 | + | _c.Call.Return(signingPublicKey) | |
| 84 | + | return _c | |
| 85 | + | } | |
| 86 | + | ||
| 87 | + | func (_c *CommitSigner_PublicKey_Call) RunAndReturn(run func() domain.SigningPublicKey) *CommitSigner_PublicKey_Call { | |
| 88 | + | _c.Call.Return(run) | |
| 89 | + | return _c | |
| 90 | + | } | |
| 91 | + | ||
| 92 | + | // Sign provides a mock function for the type CommitSigner | |
| 93 | + | func (_mock *CommitSigner) Sign(payload []byte) (string, error) { | |
| 94 | + | ret := _mock.Called(payload) | |
| 95 | + | ||
| 96 | + | if len(ret) == 0 { | |
| 97 | + | panic("no return value specified for Sign") | |
| 98 | + | } | |
| 99 | + | ||
| 100 | + | var r0 string | |
| 101 | + | var r1 error | |
| 102 | + | if returnFunc, ok := ret.Get(0).(func([]byte) (string, error)); ok { | |
| 103 | + | return returnFunc(payload) | |
| 104 | + | } | |
| 105 | + | if returnFunc, ok := ret.Get(0).(func([]byte) string); ok { | |
| 106 | + | r0 = returnFunc(payload) | |
| 107 | + | } else { | |
| 108 | + | r0 = ret.Get(0).(string) | |
| 109 | + | } | |
| 110 | + | if returnFunc, ok := ret.Get(1).(func([]byte) error); ok { | |
| 111 | + | r1 = returnFunc(payload) | |
| 112 | + | } else { | |
| 113 | + | r1 = ret.Error(1) | |
| 114 | + | } | |
| 115 | + | return r0, r1 | |
| 116 | + | } | |
| 117 | + | ||
| 118 | + | // CommitSigner_Sign_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Sign' | |
| 119 | + | type CommitSigner_Sign_Call struct { | |
| 120 | + | *mock.Call | |
| 121 | + | } | |
| 122 | + | ||
| 123 | + | // Sign is a helper method to define mock.On call | |
| 124 | + | // - payload []byte | |
| 125 | + | func (_e *CommitSigner_Expecter) Sign(payload any) *CommitSigner_Sign_Call { | |
| 126 | + | return &CommitSigner_Sign_Call{Call: _e.mock.On("Sign", payload)} | |
| 127 | + | } | |
| 128 | + | ||
| 129 | + | func (_c *CommitSigner_Sign_Call) Run(run func(payload []byte)) *CommitSigner_Sign_Call { | |
| 130 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 131 | + | var arg0 []byte | |
| 132 | + | if args[0] != nil { | |
| 133 | + | arg0 = args[0].([]byte) | |
| 134 | + | } | |
| 135 | + | run( | |
| 136 | + | arg0, | |
| 137 | + | ) | |
| 138 | + | }) | |
| 139 | + | return _c | |
| 140 | + | } | |
| 141 | + | ||
| 142 | + | func (_c *CommitSigner_Sign_Call) Return(s string, err error) *CommitSigner_Sign_Call { | |
| 143 | + | _c.Call.Return(s, err) | |
| 144 | + | return _c | |
| 145 | + | } | |
| 146 | + | ||
| 147 | + | func (_c *CommitSigner_Sign_Call) RunAndReturn(run func(payload []byte) (string, error)) *CommitSigner_Sign_Call { | |
| 148 | + | _c.Call.Return(run) | |
| 149 | + | return _c | |
| 150 | + | } | |
| @@ -0,0 +1,150 @@ | |||||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |||
| 2 | + | // github.com/vektra/mockery | |||
| 3 | + | // template: testify | |||
| 4 | + | ||||
| 5 | + | package mocks | |||
| 6 | + | ||||
| 7 | + | import ( | |||
| 8 | + | "bnema.dev/ermite/internal/domain" | |||
| 9 | + | mock "github.com/stretchr/testify/mock" | |||
| 10 | + | ) | |||
| 11 | + | ||||
| 12 | + | // NewCommitSigner creates a new instance of CommitSigner. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |||
| 13 | + | // The first argument is typically a *testing.T value. | |||
| 14 | + | func NewCommitSigner(t interface { | |||
| 15 | + | mock.TestingT | |||
| 16 | + | Cleanup(func()) | |||
| 17 | + | }) *CommitSigner { | |||
| 18 | + | if helper, ok := t.(interface{ Helper() }); ok { | |||
| 19 | + | helper.Helper() | |||
| 20 | + | } | |||
| 21 | + | ||||
| 22 | + | mock := &CommitSigner{} | |||
| 23 | + | mock.Mock.Test(t) | |||
| 24 | + | ||||
| 25 | + | t.Cleanup(func() { | |||
| 26 | + | if helper, ok := t.(interface{ Helper() }); ok { | |||
| 27 | + | helper.Helper() | |||
| 28 | + | } | |||
| 29 | + | mock.AssertExpectations(t) | |||
| 30 | + | }) | |||
| 31 | + | ||||
| 32 | + | return mock | |||
| 33 | + | } | |||
| 34 | + | ||||
| 35 | + | // CommitSigner is an autogenerated mock type for the CommitSigner type | |||
| 36 | + | type CommitSigner struct { | |||
| 37 | + | mock.Mock | |||
| 38 | + | } | |||
| 39 | + | ||||
| 40 | + | type CommitSigner_Expecter struct { | |||
| 41 | + | mock *mock.Mock | |||
| 42 | + | } | |||
| 43 | + | ||||
| 44 | + | func (_m *CommitSigner) EXPECT() *CommitSigner_Expecter { | |||
| 45 | + | return &CommitSigner_Expecter{mock: &_m.Mock} | |||
| 46 | + | } | |||
| 47 | + | ||||
| 48 | + | // PublicKey provides a mock function for the type CommitSigner | |||
| 49 | + | func (_mock *CommitSigner) PublicKey() domain.SigningPublicKey { | |||
| 50 | + | ret := _mock.Called() | |||
| 51 | + | ||||
| 52 | + | if len(ret) == 0 { | |||
| 53 | + | panic("no return value specified for PublicKey") | |||
| 54 | + | } | |||
| 55 | + | ||||
| 56 | + | var r0 domain.SigningPublicKey | |||
| 57 | + | if returnFunc, ok := ret.Get(0).(func() domain.SigningPublicKey); ok { | |||
| 58 | + | r0 = returnFunc() | |||
| 59 | + | } else { | |||
| 60 | + | r0 = ret.Get(0).(domain.SigningPublicKey) | |||
| 61 | + | } | |||
| 62 | + | return r0 | |||
| 63 | + | } | |||
| 64 | + | ||||
| 65 | + | // CommitSigner_PublicKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PublicKey' | |||
| 66 | + | type CommitSigner_PublicKey_Call struct { | |||
| 67 | + | *mock.Call | |||
| 68 | + | } | |||
| 69 | + | ||||
| 70 | + | // PublicKey is a helper method to define mock.On call | |||
| 71 | + | func (_e *CommitSigner_Expecter) PublicKey() *CommitSigner_PublicKey_Call { | |||
| 72 | + | return &CommitSigner_PublicKey_Call{Call: _e.mock.On("PublicKey")} | |||
| 73 | + | } | |||
| 74 | + | ||||
| 75 | + | func (_c *CommitSigner_PublicKey_Call) Run(run func()) *CommitSigner_PublicKey_Call { | |||
| 76 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 77 | + | run() | |||
| 78 | + | }) | |||
| 79 | + | return _c | |||
| 80 | + | } | |||
| 81 | + | ||||
| 82 | + | func (_c *CommitSigner_PublicKey_Call) Return(signingPublicKey domain.SigningPublicKey) *CommitSigner_PublicKey_Call { | |||
| 83 | + | _c.Call.Return(signingPublicKey) | |||
| 84 | + | return _c | |||
| 85 | + | } | |||
| 86 | + | ||||
| 87 | + | func (_c *CommitSigner_PublicKey_Call) RunAndReturn(run func() domain.SigningPublicKey) *CommitSigner_PublicKey_Call { | |||
| 88 | + | _c.Call.Return(run) | |||
| 89 | + | return _c | |||
| 90 | + | } | |||
| 91 | + | ||||
| 92 | + | // Sign provides a mock function for the type CommitSigner | |||
| 93 | + | func (_mock *CommitSigner) Sign(payload []byte) (string, error) { | |||
| 94 | + | ret := _mock.Called(payload) | |||
| 95 | + | ||||
| 96 | + | if len(ret) == 0 { | |||
| 97 | + | panic("no return value specified for Sign") | |||
| 98 | + | } | |||
| 99 | + | ||||
| 100 | + | var r0 string | |||
| 101 | + | var r1 error | |||
| 102 | + | if returnFunc, ok := ret.Get(0).(func([]byte) (string, error)); ok { | |||
| 103 | + | return returnFunc(payload) | |||
| 104 | + | } | |||
| 105 | + | if returnFunc, ok := ret.Get(0).(func([]byte) string); ok { | |||
| 106 | + | r0 = returnFunc(payload) | |||
| 107 | + | } else { | |||
| 108 | + | r0 = ret.Get(0).(string) | |||
| 109 | + | } | |||
| 110 | + | if returnFunc, ok := ret.Get(1).(func([]byte) error); ok { | |||
| 111 | + | r1 = returnFunc(payload) | |||
| 112 | + | } else { | |||
| 113 | + | r1 = ret.Error(1) | |||
| 114 | + | } | |||
| 115 | + | return r0, r1 | |||
| 116 | + | } | |||
| 117 | + | ||||
| 118 | + | // CommitSigner_Sign_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Sign' | |||
| 119 | + | type CommitSigner_Sign_Call struct { | |||
| 120 | + | *mock.Call | |||
| 121 | + | } | |||
| 122 | + | ||||
| 123 | + | // Sign is a helper method to define mock.On call | |||
| 124 | + | // - payload []byte | |||
| 125 | + | func (_e *CommitSigner_Expecter) Sign(payload any) *CommitSigner_Sign_Call { | |||
| 126 | + | return &CommitSigner_Sign_Call{Call: _e.mock.On("Sign", payload)} | |||
| 127 | + | } | |||
| 128 | + | ||||
| 129 | + | func (_c *CommitSigner_Sign_Call) Run(run func(payload []byte)) *CommitSigner_Sign_Call { | |||
| 130 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 131 | + | var arg0 []byte | |||
| 132 | + | if args[0] != nil { | |||
| 133 | + | arg0 = args[0].([]byte) | |||
| 134 | + | } | |||
| 135 | + | run( | |||
| 136 | + | arg0, | |||
| 137 | + | ) | |||
| 138 | + | }) | |||
| 139 | + | return _c | |||
| 140 | + | } | |||
| 141 | + | ||||
| 142 | + | func (_c *CommitSigner_Sign_Call) Return(s string, err error) *CommitSigner_Sign_Call { | |||
| 143 | + | _c.Call.Return(s, err) | |||
| 144 | + | return _c | |||
| 145 | + | } | |||
| 146 | + | ||||
| 147 | + | func (_c *CommitSigner_Sign_Call) RunAndReturn(run func(payload []byte) (string, error)) *CommitSigner_Sign_Call { | |||
| 148 | + | _c.Call.Return(run) | |||
| 149 | + | return _c | |||
| 150 | + | } | |||
A internal/ports/mocks/MergeGit.go +749 -0
| @@ -0,0 +1,749 @@ | |||
| 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 | + | // NewMergeGit creates a new instance of MergeGit. 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 NewMergeGit(t interface { | |
| 17 | + | mock.TestingT | |
| 18 | + | Cleanup(func()) | |
| 19 | + | }) *MergeGit { | |
| 20 | + | if helper, ok := t.(interface{ Helper() }); ok { | |
| 21 | + | helper.Helper() | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | mock := &MergeGit{} | |
| 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 | + | // MergeGit is an autogenerated mock type for the MergeGit type | |
| 38 | + | type MergeGit struct { | |
| 39 | + | mock.Mock | |
| 40 | + | } | |
| 41 | + | ||
| 42 | + | type MergeGit_Expecter struct { | |
| 43 | + | mock *mock.Mock | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | func (_m *MergeGit) EXPECT() *MergeGit_Expecter { | |
| 47 | + | return &MergeGit_Expecter{mock: &_m.Mock} | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | // BranchHead provides a mock function for the type MergeGit | |
| 51 | + | func (_mock *MergeGit) BranchHead(ctx context.Context, repo domain.RepoName, branch string) (domain.ObjectID, error) { | |
| 52 | + | ret := _mock.Called(ctx, repo, branch) | |
| 53 | + | ||
| 54 | + | if len(ret) == 0 { | |
| 55 | + | panic("no return value specified for BranchHead") | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | var r0 domain.ObjectID | |
| 59 | + | var r1 error | |
| 60 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, string) (domain.ObjectID, error)); ok { | |
| 61 | + | return returnFunc(ctx, repo, branch) | |
| 62 | + | } | |
| 63 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, string) domain.ObjectID); ok { | |
| 64 | + | r0 = returnFunc(ctx, repo, branch) | |
| 65 | + | } else { | |
| 66 | + | r0 = ret.Get(0).(domain.ObjectID) | |
| 67 | + | } | |
| 68 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, string) error); ok { | |
| 69 | + | r1 = returnFunc(ctx, repo, branch) | |
| 70 | + | } else { | |
| 71 | + | r1 = ret.Error(1) | |
| 72 | + | } | |
| 73 | + | return r0, r1 | |
| 74 | + | } | |
| 75 | + | ||
| 76 | + | // MergeGit_BranchHead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BranchHead' | |
| 77 | + | type MergeGit_BranchHead_Call struct { | |
| 78 | + | *mock.Call | |
| 79 | + | } | |
| 80 | + | ||
| 81 | + | // BranchHead is a helper method to define mock.On call | |
| 82 | + | // - ctx context.Context | |
| 83 | + | // - repo domain.RepoName | |
| 84 | + | // - branch string | |
| 85 | + | func (_e *MergeGit_Expecter) BranchHead(ctx any, repo any, branch any) *MergeGit_BranchHead_Call { | |
| 86 | + | return &MergeGit_BranchHead_Call{Call: _e.mock.On("BranchHead", ctx, repo, branch)} | |
| 87 | + | } | |
| 88 | + | ||
| 89 | + | func (_c *MergeGit_BranchHead_Call) Run(run func(ctx context.Context, repo domain.RepoName, branch string)) *MergeGit_BranchHead_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.RepoName | |
| 96 | + | if args[1] != nil { | |
| 97 | + | arg1 = args[1].(domain.RepoName) | |
| 98 | + | } | |
| 99 | + | var arg2 string | |
| 100 | + | if args[2] != nil { | |
| 101 | + | arg2 = args[2].(string) | |
| 102 | + | } | |
| 103 | + | run( | |
| 104 | + | arg0, | |
| 105 | + | arg1, | |
| 106 | + | arg2, | |
| 107 | + | ) | |
| 108 | + | }) | |
| 109 | + | return _c | |
| 110 | + | } | |
| 111 | + | ||
| 112 | + | func (_c *MergeGit_BranchHead_Call) Return(objectID domain.ObjectID, err error) *MergeGit_BranchHead_Call { | |
| 113 | + | _c.Call.Return(objectID, err) | |
| 114 | + | return _c | |
| 115 | + | } | |
| 116 | + | ||
| 117 | + | func (_c *MergeGit_BranchHead_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, branch string) (domain.ObjectID, error)) *MergeGit_BranchHead_Call { | |
| 118 | + | _c.Call.Return(run) | |
| 119 | + | return _c | |
| 120 | + | } | |
| 121 | + | ||
| 122 | + | // CommitTree provides a mock function for the type MergeGit | |
| 123 | + | func (_mock *MergeGit) CommitTree(ctx context.Context, repo domain.RepoName, id domain.ObjectID) (domain.ObjectID, error) { | |
| 124 | + | ret := _mock.Called(ctx, repo, id) | |
| 125 | + | ||
| 126 | + | if len(ret) == 0 { | |
| 127 | + | panic("no return value specified for CommitTree") | |
| 128 | + | } | |
| 129 | + | ||
| 130 | + | var r0 domain.ObjectID | |
| 131 | + | var r1 error | |
| 132 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID) (domain.ObjectID, error)); ok { | |
| 133 | + | return returnFunc(ctx, repo, id) | |
| 134 | + | } | |
| 135 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID) domain.ObjectID); ok { | |
| 136 | + | r0 = returnFunc(ctx, repo, id) | |
| 137 | + | } else { | |
| 138 | + | r0 = ret.Get(0).(domain.ObjectID) | |
| 139 | + | } | |
| 140 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, domain.ObjectID) error); ok { | |
| 141 | + | r1 = returnFunc(ctx, repo, id) | |
| 142 | + | } else { | |
| 143 | + | r1 = ret.Error(1) | |
| 144 | + | } | |
| 145 | + | return r0, r1 | |
| 146 | + | } | |
| 147 | + | ||
| 148 | + | // MergeGit_CommitTree_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CommitTree' | |
| 149 | + | type MergeGit_CommitTree_Call struct { | |
| 150 | + | *mock.Call | |
| 151 | + | } | |
| 152 | + | ||
| 153 | + | // CommitTree is a helper method to define mock.On call | |
| 154 | + | // - ctx context.Context | |
| 155 | + | // - repo domain.RepoName | |
| 156 | + | // - id domain.ObjectID | |
| 157 | + | func (_e *MergeGit_Expecter) CommitTree(ctx any, repo any, id any) *MergeGit_CommitTree_Call { | |
| 158 | + | return &MergeGit_CommitTree_Call{Call: _e.mock.On("CommitTree", ctx, repo, id)} | |
| 159 | + | } | |
| 160 | + | ||
| 161 | + | func (_c *MergeGit_CommitTree_Call) Run(run func(ctx context.Context, repo domain.RepoName, id domain.ObjectID)) *MergeGit_CommitTree_Call { | |
| 162 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 163 | + | var arg0 context.Context | |
| 164 | + | if args[0] != nil { | |
| 165 | + | arg0 = args[0].(context.Context) | |
| 166 | + | } | |
| 167 | + | var arg1 domain.RepoName | |
| 168 | + | if args[1] != nil { | |
| 169 | + | arg1 = args[1].(domain.RepoName) | |
| 170 | + | } | |
| 171 | + | var arg2 domain.ObjectID | |
| 172 | + | if args[2] != nil { | |
| 173 | + | arg2 = args[2].(domain.ObjectID) | |
| 174 | + | } | |
| 175 | + | run( | |
| 176 | + | arg0, | |
| 177 | + | arg1, | |
| 178 | + | arg2, | |
| 179 | + | ) | |
| 180 | + | }) | |
| 181 | + | return _c | |
| 182 | + | } | |
| 183 | + | ||
| 184 | + | func (_c *MergeGit_CommitTree_Call) Return(objectID domain.ObjectID, err error) *MergeGit_CommitTree_Call { | |
| 185 | + | _c.Call.Return(objectID, err) | |
| 186 | + | return _c | |
| 187 | + | } | |
| 188 | + | ||
| 189 | + | func (_c *MergeGit_CommitTree_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, id domain.ObjectID) (domain.ObjectID, error)) *MergeGit_CommitTree_Call { | |
| 190 | + | _c.Call.Return(run) | |
| 191 | + | return _c | |
| 192 | + | } | |
| 193 | + | ||
| 194 | + | // DiffCommits provides a mock function for the type MergeGit | |
| 195 | + | func (_mock *MergeGit) DiffCommits(ctx context.Context, repo domain.RepoName, base domain.ObjectID, head domain.ObjectID, limits domain.DiffLimits) (domain.DiffStats, domain.Diff, error) { | |
| 196 | + | ret := _mock.Called(ctx, repo, base, head, limits) | |
| 197 | + | ||
| 198 | + | if len(ret) == 0 { | |
| 199 | + | panic("no return value specified for DiffCommits") | |
| 200 | + | } | |
| 201 | + | ||
| 202 | + | var r0 domain.DiffStats | |
| 203 | + | var r1 domain.Diff | |
| 204 | + | var r2 error | |
| 205 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID, domain.DiffLimits) (domain.DiffStats, domain.Diff, error)); ok { | |
| 206 | + | return returnFunc(ctx, repo, base, head, limits) | |
| 207 | + | } | |
| 208 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID, domain.DiffLimits) domain.DiffStats); ok { | |
| 209 | + | r0 = returnFunc(ctx, repo, base, head, limits) | |
| 210 | + | } else { | |
| 211 | + | r0 = ret.Get(0).(domain.DiffStats) | |
| 212 | + | } | |
| 213 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID, domain.DiffLimits) domain.Diff); ok { | |
| 214 | + | r1 = returnFunc(ctx, repo, base, head, limits) | |
| 215 | + | } else { | |
| 216 | + | r1 = ret.Get(1).(domain.Diff) | |
| 217 | + | } | |
| 218 | + | if returnFunc, ok := ret.Get(2).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID, domain.DiffLimits) error); ok { | |
| 219 | + | r2 = returnFunc(ctx, repo, base, head, limits) | |
| 220 | + | } else { | |
| 221 | + | r2 = ret.Error(2) | |
| 222 | + | } | |
| 223 | + | return r0, r1, r2 | |
| 224 | + | } | |
| 225 | + | ||
| 226 | + | // MergeGit_DiffCommits_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DiffCommits' | |
| 227 | + | type MergeGit_DiffCommits_Call struct { | |
| 228 | + | *mock.Call | |
| 229 | + | } | |
| 230 | + | ||
| 231 | + | // DiffCommits is a helper method to define mock.On call | |
| 232 | + | // - ctx context.Context | |
| 233 | + | // - repo domain.RepoName | |
| 234 | + | // - base domain.ObjectID | |
| 235 | + | // - head domain.ObjectID | |
| 236 | + | // - limits domain.DiffLimits | |
| 237 | + | func (_e *MergeGit_Expecter) DiffCommits(ctx any, repo any, base any, head any, limits any) *MergeGit_DiffCommits_Call { | |
| 238 | + | return &MergeGit_DiffCommits_Call{Call: _e.mock.On("DiffCommits", ctx, repo, base, head, limits)} | |
| 239 | + | } | |
| 240 | + | ||
| 241 | + | func (_c *MergeGit_DiffCommits_Call) Run(run func(ctx context.Context, repo domain.RepoName, base domain.ObjectID, head domain.ObjectID, limits domain.DiffLimits)) *MergeGit_DiffCommits_Call { | |
| 242 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 243 | + | var arg0 context.Context | |
| 244 | + | if args[0] != nil { | |
| 245 | + | arg0 = args[0].(context.Context) | |
| 246 | + | } | |
| 247 | + | var arg1 domain.RepoName | |
| 248 | + | if args[1] != nil { | |
| 249 | + | arg1 = args[1].(domain.RepoName) | |
| 250 | + | } | |
| 251 | + | var arg2 domain.ObjectID | |
| 252 | + | if args[2] != nil { | |
| 253 | + | arg2 = args[2].(domain.ObjectID) | |
| 254 | + | } | |
| 255 | + | var arg3 domain.ObjectID | |
| 256 | + | if args[3] != nil { | |
| 257 | + | arg3 = args[3].(domain.ObjectID) | |
| 258 | + | } | |
| 259 | + | var arg4 domain.DiffLimits | |
| 260 | + | if args[4] != nil { | |
| 261 | + | arg4 = args[4].(domain.DiffLimits) | |
| 262 | + | } | |
| 263 | + | run( | |
| 264 | + | arg0, | |
| 265 | + | arg1, | |
| 266 | + | arg2, | |
| 267 | + | arg3, | |
| 268 | + | arg4, | |
| 269 | + | ) | |
| 270 | + | }) | |
| 271 | + | return _c | |
| 272 | + | } | |
| 273 | + | ||
| 274 | + | func (_c *MergeGit_DiffCommits_Call) Return(diffStats domain.DiffStats, diff domain.Diff, err error) *MergeGit_DiffCommits_Call { | |
| 275 | + | _c.Call.Return(diffStats, diff, err) | |
| 276 | + | return _c | |
| 277 | + | } | |
| 278 | + | ||
| 279 | + | func (_c *MergeGit_DiffCommits_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, base domain.ObjectID, head domain.ObjectID, limits domain.DiffLimits) (domain.DiffStats, domain.Diff, error)) *MergeGit_DiffCommits_Call { | |
| 280 | + | _c.Call.Return(run) | |
| 281 | + | return _c | |
| 282 | + | } | |
| 283 | + | ||
| 284 | + | // HasCommit provides a mock function for the type MergeGit | |
| 285 | + | func (_mock *MergeGit) HasCommit(ctx context.Context, repo domain.RepoName, id domain.ObjectID) (bool, error) { | |
| 286 | + | ret := _mock.Called(ctx, repo, id) | |
| 287 | + | ||
| 288 | + | if len(ret) == 0 { | |
| 289 | + | panic("no return value specified for HasCommit") | |
| 290 | + | } | |
| 291 | + | ||
| 292 | + | var r0 bool | |
| 293 | + | var r1 error | |
| 294 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID) (bool, error)); ok { | |
| 295 | + | return returnFunc(ctx, repo, id) | |
| 296 | + | } | |
| 297 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID) bool); ok { | |
| 298 | + | r0 = returnFunc(ctx, repo, id) | |
| 299 | + | } else { | |
| 300 | + | r0 = ret.Get(0).(bool) | |
| 301 | + | } | |
| 302 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, domain.ObjectID) error); ok { | |
| 303 | + | r1 = returnFunc(ctx, repo, id) | |
| 304 | + | } else { | |
| 305 | + | r1 = ret.Error(1) | |
| 306 | + | } | |
| 307 | + | return r0, r1 | |
| 308 | + | } | |
| 309 | + | ||
| 310 | + | // MergeGit_HasCommit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HasCommit' | |
| 311 | + | type MergeGit_HasCommit_Call struct { | |
| 312 | + | *mock.Call | |
| 313 | + | } | |
| 314 | + | ||
| 315 | + | // HasCommit is a helper method to define mock.On call | |
| 316 | + | // - ctx context.Context | |
| 317 | + | // - repo domain.RepoName | |
| 318 | + | // - id domain.ObjectID | |
| 319 | + | func (_e *MergeGit_Expecter) HasCommit(ctx any, repo any, id any) *MergeGit_HasCommit_Call { | |
| 320 | + | return &MergeGit_HasCommit_Call{Call: _e.mock.On("HasCommit", ctx, repo, id)} | |
| 321 | + | } | |
| 322 | + | ||
| 323 | + | func (_c *MergeGit_HasCommit_Call) Run(run func(ctx context.Context, repo domain.RepoName, id domain.ObjectID)) *MergeGit_HasCommit_Call { | |
| 324 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 325 | + | var arg0 context.Context | |
| 326 | + | if args[0] != nil { | |
| 327 | + | arg0 = args[0].(context.Context) | |
| 328 | + | } | |
| 329 | + | var arg1 domain.RepoName | |
| 330 | + | if args[1] != nil { | |
| 331 | + | arg1 = args[1].(domain.RepoName) | |
| 332 | + | } | |
| 333 | + | var arg2 domain.ObjectID | |
| 334 | + | if args[2] != nil { | |
| 335 | + | arg2 = args[2].(domain.ObjectID) | |
| 336 | + | } | |
| 337 | + | run( | |
| 338 | + | arg0, | |
| 339 | + | arg1, | |
| 340 | + | arg2, | |
| 341 | + | ) | |
| 342 | + | }) | |
| 343 | + | return _c | |
| 344 | + | } | |
| 345 | + | ||
| 346 | + | func (_c *MergeGit_HasCommit_Call) Return(b bool, err error) *MergeGit_HasCommit_Call { | |
| 347 | + | _c.Call.Return(b, err) | |
| 348 | + | return _c | |
| 349 | + | } | |
| 350 | + | ||
| 351 | + | func (_c *MergeGit_HasCommit_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, id domain.ObjectID) (bool, error)) *MergeGit_HasCommit_Call { | |
| 352 | + | _c.Call.Return(run) | |
| 353 | + | return _c | |
| 354 | + | } | |
| 355 | + | ||
| 356 | + | // IsAncestor provides a mock function for the type MergeGit | |
| 357 | + | func (_mock *MergeGit) IsAncestor(ctx context.Context, repo domain.RepoName, ancestor domain.ObjectID, descendant domain.ObjectID) (bool, error) { | |
| 358 | + | ret := _mock.Called(ctx, repo, ancestor, descendant) | |
| 359 | + | ||
| 360 | + | if len(ret) == 0 { | |
| 361 | + | panic("no return value specified for IsAncestor") | |
| 362 | + | } | |
| 363 | + | ||
| 364 | + | var r0 bool | |
| 365 | + | var r1 error | |
| 366 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID) (bool, error)); ok { | |
| 367 | + | return returnFunc(ctx, repo, ancestor, descendant) | |
| 368 | + | } | |
| 369 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID) bool); ok { | |
| 370 | + | r0 = returnFunc(ctx, repo, ancestor, descendant) | |
| 371 | + | } else { | |
| 372 | + | r0 = ret.Get(0).(bool) | |
| 373 | + | } | |
| 374 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID) error); ok { | |
| 375 | + | r1 = returnFunc(ctx, repo, ancestor, descendant) | |
| 376 | + | } else { | |
| 377 | + | r1 = ret.Error(1) | |
| 378 | + | } | |
| 379 | + | return r0, r1 | |
| 380 | + | } | |
| 381 | + | ||
| 382 | + | // MergeGit_IsAncestor_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsAncestor' | |
| 383 | + | type MergeGit_IsAncestor_Call struct { | |
| 384 | + | *mock.Call | |
| 385 | + | } | |
| 386 | + | ||
| 387 | + | // IsAncestor is a helper method to define mock.On call | |
| 388 | + | // - ctx context.Context | |
| 389 | + | // - repo domain.RepoName | |
| 390 | + | // - ancestor domain.ObjectID | |
| 391 | + | // - descendant domain.ObjectID | |
| 392 | + | func (_e *MergeGit_Expecter) IsAncestor(ctx any, repo any, ancestor any, descendant any) *MergeGit_IsAncestor_Call { | |
| 393 | + | return &MergeGit_IsAncestor_Call{Call: _e.mock.On("IsAncestor", ctx, repo, ancestor, descendant)} | |
| 394 | + | } | |
| 395 | + | ||
| 396 | + | func (_c *MergeGit_IsAncestor_Call) Run(run func(ctx context.Context, repo domain.RepoName, ancestor domain.ObjectID, descendant domain.ObjectID)) *MergeGit_IsAncestor_Call { | |
| 397 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 398 | + | var arg0 context.Context | |
| 399 | + | if args[0] != nil { | |
| 400 | + | arg0 = args[0].(context.Context) | |
| 401 | + | } | |
| 402 | + | var arg1 domain.RepoName | |
| 403 | + | if args[1] != nil { | |
| 404 | + | arg1 = args[1].(domain.RepoName) | |
| 405 | + | } | |
| 406 | + | var arg2 domain.ObjectID | |
| 407 | + | if args[2] != nil { | |
| 408 | + | arg2 = args[2].(domain.ObjectID) | |
| 409 | + | } | |
| 410 | + | var arg3 domain.ObjectID | |
| 411 | + | if args[3] != nil { | |
| 412 | + | arg3 = args[3].(domain.ObjectID) | |
| 413 | + | } | |
| 414 | + | run( | |
| 415 | + | arg0, | |
| 416 | + | arg1, | |
| 417 | + | arg2, | |
| 418 | + | arg3, | |
| 419 | + | ) | |
| 420 | + | }) | |
| 421 | + | return _c | |
| 422 | + | } | |
| 423 | + | ||
| 424 | + | func (_c *MergeGit_IsAncestor_Call) Return(b bool, err error) *MergeGit_IsAncestor_Call { | |
| 425 | + | _c.Call.Return(b, err) | |
| 426 | + | return _c | |
| 427 | + | } | |
| 428 | + | ||
| 429 | + | func (_c *MergeGit_IsAncestor_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, ancestor domain.ObjectID, descendant domain.ObjectID) (bool, error)) *MergeGit_IsAncestor_Call { | |
| 430 | + | _c.Call.Return(run) | |
| 431 | + | return _c | |
| 432 | + | } | |
| 433 | + | ||
| 434 | + | // ListRange provides a mock function for the type MergeGit | |
| 435 | + | func (_mock *MergeGit) ListRange(ctx context.Context, repo domain.RepoName, exclude domain.ObjectID, head domain.ObjectID, limit int) ([]domain.Commit, bool, error) { | |
| 436 | + | ret := _mock.Called(ctx, repo, exclude, head, limit) | |
| 437 | + | ||
| 438 | + | if len(ret) == 0 { | |
| 439 | + | panic("no return value specified for ListRange") | |
| 440 | + | } | |
| 441 | + | ||
| 442 | + | var r0 []domain.Commit | |
| 443 | + | var r1 bool | |
| 444 | + | var r2 error | |
| 445 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID, int) ([]domain.Commit, bool, error)); ok { | |
| 446 | + | return returnFunc(ctx, repo, exclude, head, limit) | |
| 447 | + | } | |
| 448 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID, int) []domain.Commit); ok { | |
| 449 | + | r0 = returnFunc(ctx, repo, exclude, head, limit) | |
| 450 | + | } else { | |
| 451 | + | if ret.Get(0) != nil { | |
| 452 | + | r0 = ret.Get(0).([]domain.Commit) | |
| 453 | + | } | |
| 454 | + | } | |
| 455 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID, int) bool); ok { | |
| 456 | + | r1 = returnFunc(ctx, repo, exclude, head, limit) | |
| 457 | + | } else { | |
| 458 | + | r1 = ret.Get(1).(bool) | |
| 459 | + | } | |
| 460 | + | if returnFunc, ok := ret.Get(2).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID, int) error); ok { | |
| 461 | + | r2 = returnFunc(ctx, repo, exclude, head, limit) | |
| 462 | + | } else { | |
| 463 | + | r2 = ret.Error(2) | |
| 464 | + | } | |
| 465 | + | return r0, r1, r2 | |
| 466 | + | } | |
| 467 | + | ||
| 468 | + | // MergeGit_ListRange_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListRange' | |
| 469 | + | type MergeGit_ListRange_Call struct { | |
| 470 | + | *mock.Call | |
| 471 | + | } | |
| 472 | + | ||
| 473 | + | // ListRange is a helper method to define mock.On call | |
| 474 | + | // - ctx context.Context | |
| 475 | + | // - repo domain.RepoName | |
| 476 | + | // - exclude domain.ObjectID | |
| 477 | + | // - head domain.ObjectID | |
| 478 | + | // - limit int | |
| 479 | + | func (_e *MergeGit_Expecter) ListRange(ctx any, repo any, exclude any, head any, limit any) *MergeGit_ListRange_Call { | |
| 480 | + | return &MergeGit_ListRange_Call{Call: _e.mock.On("ListRange", ctx, repo, exclude, head, limit)} | |
| 481 | + | } | |
| 482 | + | ||
| 483 | + | func (_c *MergeGit_ListRange_Call) Run(run func(ctx context.Context, repo domain.RepoName, exclude domain.ObjectID, head domain.ObjectID, limit int)) *MergeGit_ListRange_Call { | |
| 484 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 485 | + | var arg0 context.Context | |
| 486 | + | if args[0] != nil { | |
| 487 | + | arg0 = args[0].(context.Context) | |
| 488 | + | } | |
| 489 | + | var arg1 domain.RepoName | |
| 490 | + | if args[1] != nil { | |
| 491 | + | arg1 = args[1].(domain.RepoName) | |
| 492 | + | } | |
| 493 | + | var arg2 domain.ObjectID | |
| 494 | + | if args[2] != nil { | |
| 495 | + | arg2 = args[2].(domain.ObjectID) | |
| 496 | + | } | |
| 497 | + | var arg3 domain.ObjectID | |
| 498 | + | if args[3] != nil { | |
| 499 | + | arg3 = args[3].(domain.ObjectID) | |
| 500 | + | } | |
| 501 | + | var arg4 int | |
| 502 | + | if args[4] != nil { | |
| 503 | + | arg4 = args[4].(int) | |
| 504 | + | } | |
| 505 | + | run( | |
| 506 | + | arg0, | |
| 507 | + | arg1, | |
| 508 | + | arg2, | |
| 509 | + | arg3, | |
| 510 | + | arg4, | |
| 511 | + | ) | |
| 512 | + | }) | |
| 513 | + | return _c | |
| 514 | + | } | |
| 515 | + | ||
| 516 | + | func (_c *MergeGit_ListRange_Call) Return(commits []domain.Commit, more bool, err error) *MergeGit_ListRange_Call { | |
| 517 | + | _c.Call.Return(commits, more, err) | |
| 518 | + | return _c | |
| 519 | + | } | |
| 520 | + | ||
| 521 | + | func (_c *MergeGit_ListRange_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, exclude domain.ObjectID, head domain.ObjectID, limit int) ([]domain.Commit, bool, error)) *MergeGit_ListRange_Call { | |
| 522 | + | _c.Call.Return(run) | |
| 523 | + | return _c | |
| 524 | + | } | |
| 525 | + | ||
| 526 | + | // MergeBase provides a mock function for the type MergeGit | |
| 527 | + | func (_mock *MergeGit) MergeBase(ctx context.Context, repo domain.RepoName, a domain.ObjectID, b domain.ObjectID) (domain.ObjectID, error) { | |
| 528 | + | ret := _mock.Called(ctx, repo, a, b) | |
| 529 | + | ||
| 530 | + | if len(ret) == 0 { | |
| 531 | + | panic("no return value specified for MergeBase") | |
| 532 | + | } | |
| 533 | + | ||
| 534 | + | var r0 domain.ObjectID | |
| 535 | + | var r1 error | |
| 536 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID) (domain.ObjectID, error)); ok { | |
| 537 | + | return returnFunc(ctx, repo, a, b) | |
| 538 | + | } | |
| 539 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID) domain.ObjectID); ok { | |
| 540 | + | r0 = returnFunc(ctx, repo, a, b) | |
| 541 | + | } else { | |
| 542 | + | r0 = ret.Get(0).(domain.ObjectID) | |
| 543 | + | } | |
| 544 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID) error); ok { | |
| 545 | + | r1 = returnFunc(ctx, repo, a, b) | |
| 546 | + | } else { | |
| 547 | + | r1 = ret.Error(1) | |
| 548 | + | } | |
| 549 | + | return r0, r1 | |
| 550 | + | } | |
| 551 | + | ||
| 552 | + | // MergeGit_MergeBase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MergeBase' | |
| 553 | + | type MergeGit_MergeBase_Call struct { | |
| 554 | + | *mock.Call | |
| 555 | + | } | |
| 556 | + | ||
| 557 | + | // MergeBase is a helper method to define mock.On call | |
| 558 | + | // - ctx context.Context | |
| 559 | + | // - repo domain.RepoName | |
| 560 | + | // - a domain.ObjectID | |
| 561 | + | // - b domain.ObjectID | |
| 562 | + | func (_e *MergeGit_Expecter) MergeBase(ctx any, repo any, a any, b any) *MergeGit_MergeBase_Call { | |
| 563 | + | return &MergeGit_MergeBase_Call{Call: _e.mock.On("MergeBase", ctx, repo, a, b)} | |
| 564 | + | } | |
| 565 | + | ||
| 566 | + | func (_c *MergeGit_MergeBase_Call) Run(run func(ctx context.Context, repo domain.RepoName, a domain.ObjectID, b domain.ObjectID)) *MergeGit_MergeBase_Call { | |
| 567 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 568 | + | var arg0 context.Context | |
| 569 | + | if args[0] != nil { | |
| 570 | + | arg0 = args[0].(context.Context) | |
| 571 | + | } | |
| 572 | + | var arg1 domain.RepoName | |
| 573 | + | if args[1] != nil { | |
| 574 | + | arg1 = args[1].(domain.RepoName) | |
| 575 | + | } | |
| 576 | + | var arg2 domain.ObjectID | |
| 577 | + | if args[2] != nil { | |
| 578 | + | arg2 = args[2].(domain.ObjectID) | |
| 579 | + | } | |
| 580 | + | var arg3 domain.ObjectID | |
| 581 | + | if args[3] != nil { | |
| 582 | + | arg3 = args[3].(domain.ObjectID) | |
| 583 | + | } | |
| 584 | + | run( | |
| 585 | + | arg0, | |
| 586 | + | arg1, | |
| 587 | + | arg2, | |
| 588 | + | arg3, | |
| 589 | + | ) | |
| 590 | + | }) | |
| 591 | + | return _c | |
| 592 | + | } | |
| 593 | + | ||
| 594 | + | func (_c *MergeGit_MergeBase_Call) Return(objectID domain.ObjectID, err error) *MergeGit_MergeBase_Call { | |
| 595 | + | _c.Call.Return(objectID, err) | |
| 596 | + | return _c | |
| 597 | + | } | |
| 598 | + | ||
| 599 | + | func (_c *MergeGit_MergeBase_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, a domain.ObjectID, b domain.ObjectID) (domain.ObjectID, error)) *MergeGit_MergeBase_Call { | |
| 600 | + | _c.Call.Return(run) | |
| 601 | + | return _c | |
| 602 | + | } | |
| 603 | + | ||
| 604 | + | // UpdateBranch provides a mock function for the type MergeGit | |
| 605 | + | func (_mock *MergeGit) UpdateBranch(ctx context.Context, repo domain.RepoName, branch string, old domain.ObjectID, new domain.ObjectID) error { | |
| 606 | + | ret := _mock.Called(ctx, repo, branch, old, new) | |
| 607 | + | ||
| 608 | + | if len(ret) == 0 { | |
| 609 | + | panic("no return value specified for UpdateBranch") | |
| 610 | + | } | |
| 611 | + | ||
| 612 | + | var r0 error | |
| 613 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, string, domain.ObjectID, domain.ObjectID) error); ok { | |
| 614 | + | r0 = returnFunc(ctx, repo, branch, old, new) | |
| 615 | + | } else { | |
| 616 | + | r0 = ret.Error(0) | |
| 617 | + | } | |
| 618 | + | return r0 | |
| 619 | + | } | |
| 620 | + | ||
| 621 | + | // MergeGit_UpdateBranch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateBranch' | |
| 622 | + | type MergeGit_UpdateBranch_Call struct { | |
| 623 | + | *mock.Call | |
| 624 | + | } | |
| 625 | + | ||
| 626 | + | // UpdateBranch is a helper method to define mock.On call | |
| 627 | + | // - ctx context.Context | |
| 628 | + | // - repo domain.RepoName | |
| 629 | + | // - branch string | |
| 630 | + | // - old domain.ObjectID | |
| 631 | + | // - new domain.ObjectID | |
| 632 | + | func (_e *MergeGit_Expecter) UpdateBranch(ctx any, repo any, branch any, old any, new any) *MergeGit_UpdateBranch_Call { | |
| 633 | + | return &MergeGit_UpdateBranch_Call{Call: _e.mock.On("UpdateBranch", ctx, repo, branch, old, new)} | |
| 634 | + | } | |
| 635 | + | ||
| 636 | + | func (_c *MergeGit_UpdateBranch_Call) Run(run func(ctx context.Context, repo domain.RepoName, branch string, old domain.ObjectID, new domain.ObjectID)) *MergeGit_UpdateBranch_Call { | |
| 637 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 638 | + | var arg0 context.Context | |
| 639 | + | if args[0] != nil { | |
| 640 | + | arg0 = args[0].(context.Context) | |
| 641 | + | } | |
| 642 | + | var arg1 domain.RepoName | |
| 643 | + | if args[1] != nil { | |
| 644 | + | arg1 = args[1].(domain.RepoName) | |
| 645 | + | } | |
| 646 | + | var arg2 string | |
| 647 | + | if args[2] != nil { | |
| 648 | + | arg2 = args[2].(string) | |
| 649 | + | } | |
| 650 | + | var arg3 domain.ObjectID | |
| 651 | + | if args[3] != nil { | |
| 652 | + | arg3 = args[3].(domain.ObjectID) | |
| 653 | + | } | |
| 654 | + | var arg4 domain.ObjectID | |
| 655 | + | if args[4] != nil { | |
| 656 | + | arg4 = args[4].(domain.ObjectID) | |
| 657 | + | } | |
| 658 | + | run( | |
| 659 | + | arg0, | |
| 660 | + | arg1, | |
| 661 | + | arg2, | |
| 662 | + | arg3, | |
| 663 | + | arg4, | |
| 664 | + | ) | |
| 665 | + | }) | |
| 666 | + | return _c | |
| 667 | + | } | |
| 668 | + | ||
| 669 | + | func (_c *MergeGit_UpdateBranch_Call) Return(err error) *MergeGit_UpdateBranch_Call { | |
| 670 | + | _c.Call.Return(err) | |
| 671 | + | return _c | |
| 672 | + | } | |
| 673 | + | ||
| 674 | + | func (_c *MergeGit_UpdateBranch_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, branch string, old domain.ObjectID, new domain.ObjectID) error) *MergeGit_UpdateBranch_Call { | |
| 675 | + | _c.Call.Return(run) | |
| 676 | + | return _c | |
| 677 | + | } | |
| 678 | + | ||
| 679 | + | // WriteCommit provides a mock function for the type MergeGit | |
| 680 | + | func (_mock *MergeGit) WriteCommit(ctx context.Context, repo domain.RepoName, object []byte) (domain.ObjectID, error) { | |
| 681 | + | ret := _mock.Called(ctx, repo, object) | |
| 682 | + | ||
| 683 | + | if len(ret) == 0 { | |
| 684 | + | panic("no return value specified for WriteCommit") | |
| 685 | + | } | |
| 686 | + | ||
| 687 | + | var r0 domain.ObjectID | |
| 688 | + | var r1 error | |
| 689 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, []byte) (domain.ObjectID, error)); ok { | |
| 690 | + | return returnFunc(ctx, repo, object) | |
| 691 | + | } | |
| 692 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, []byte) domain.ObjectID); ok { | |
| 693 | + | r0 = returnFunc(ctx, repo, object) | |
| 694 | + | } else { | |
| 695 | + | r0 = ret.Get(0).(domain.ObjectID) | |
| 696 | + | } | |
| 697 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, []byte) error); ok { | |
| 698 | + | r1 = returnFunc(ctx, repo, object) | |
| 699 | + | } else { | |
| 700 | + | r1 = ret.Error(1) | |
| 701 | + | } | |
| 702 | + | return r0, r1 | |
| 703 | + | } | |
| 704 | + | ||
| 705 | + | // MergeGit_WriteCommit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteCommit' | |
| 706 | + | type MergeGit_WriteCommit_Call struct { | |
| 707 | + | *mock.Call | |
| 708 | + | } | |
| 709 | + | ||
| 710 | + | // WriteCommit is a helper method to define mock.On call | |
| 711 | + | // - ctx context.Context | |
| 712 | + | // - repo domain.RepoName | |
| 713 | + | // - object []byte | |
| 714 | + | func (_e *MergeGit_Expecter) WriteCommit(ctx any, repo any, object any) *MergeGit_WriteCommit_Call { | |
| 715 | + | return &MergeGit_WriteCommit_Call{Call: _e.mock.On("WriteCommit", ctx, repo, object)} | |
| 716 | + | } | |
| 717 | + | ||
| 718 | + | func (_c *MergeGit_WriteCommit_Call) Run(run func(ctx context.Context, repo domain.RepoName, object []byte)) *MergeGit_WriteCommit_Call { | |
| 719 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 720 | + | var arg0 context.Context | |
| 721 | + | if args[0] != nil { | |
| 722 | + | arg0 = args[0].(context.Context) | |
| 723 | + | } | |
| 724 | + | var arg1 domain.RepoName | |
| 725 | + | if args[1] != nil { | |
| 726 | + | arg1 = args[1].(domain.RepoName) | |
| 727 | + | } | |
| 728 | + | var arg2 []byte | |
| 729 | + | if args[2] != nil { | |
| 730 | + | arg2 = args[2].([]byte) | |
| 731 | + | } | |
| 732 | + | run( | |
| 733 | + | arg0, | |
| 734 | + | arg1, | |
| 735 | + | arg2, | |
| 736 | + | ) | |
| 737 | + | }) | |
| 738 | + | return _c | |
| 739 | + | } | |
| 740 | + | ||
| 741 | + | func (_c *MergeGit_WriteCommit_Call) Return(objectID domain.ObjectID, err error) *MergeGit_WriteCommit_Call { | |
| 742 | + | _c.Call.Return(objectID, err) | |
| 743 | + | return _c | |
| 744 | + | } | |
| 745 | + | ||
| 746 | + | func (_c *MergeGit_WriteCommit_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, object []byte) (domain.ObjectID, error)) *MergeGit_WriteCommit_Call { | |
| 747 | + | _c.Call.Return(run) | |
| 748 | + | return _c | |
| 749 | + | } | |
| @@ -0,0 +1,749 @@ | |||||
| 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 | + | // NewMergeGit creates a new instance of MergeGit. 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 NewMergeGit(t interface { | |||
| 17 | + | mock.TestingT | |||
| 18 | + | Cleanup(func()) | |||
| 19 | + | }) *MergeGit { | |||
| 20 | + | if helper, ok := t.(interface{ Helper() }); ok { | |||
| 21 | + | helper.Helper() | |||
| 22 | + | } | |||
| 23 | + | ||||
| 24 | + | mock := &MergeGit{} | |||
| 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 | + | // MergeGit is an autogenerated mock type for the MergeGit type | |||
| 38 | + | type MergeGit struct { | |||
| 39 | + | mock.Mock | |||
| 40 | + | } | |||
| 41 | + | ||||
| 42 | + | type MergeGit_Expecter struct { | |||
| 43 | + | mock *mock.Mock | |||
| 44 | + | } | |||
| 45 | + | ||||
| 46 | + | func (_m *MergeGit) EXPECT() *MergeGit_Expecter { | |||
| 47 | + | return &MergeGit_Expecter{mock: &_m.Mock} | |||
| 48 | + | } | |||
| 49 | + | ||||
| 50 | + | // BranchHead provides a mock function for the type MergeGit | |||
| 51 | + | func (_mock *MergeGit) BranchHead(ctx context.Context, repo domain.RepoName, branch string) (domain.ObjectID, error) { | |||
| 52 | + | ret := _mock.Called(ctx, repo, branch) | |||
| 53 | + | ||||
| 54 | + | if len(ret) == 0 { | |||
| 55 | + | panic("no return value specified for BranchHead") | |||
| 56 | + | } | |||
| 57 | + | ||||
| 58 | + | var r0 domain.ObjectID | |||
| 59 | + | var r1 error | |||
| 60 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, string) (domain.ObjectID, error)); ok { | |||
| 61 | + | return returnFunc(ctx, repo, branch) | |||
| 62 | + | } | |||
| 63 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, string) domain.ObjectID); ok { | |||
| 64 | + | r0 = returnFunc(ctx, repo, branch) | |||
| 65 | + | } else { | |||
| 66 | + | r0 = ret.Get(0).(domain.ObjectID) | |||
| 67 | + | } | |||
| 68 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, string) error); ok { | |||
| 69 | + | r1 = returnFunc(ctx, repo, branch) | |||
| 70 | + | } else { | |||
| 71 | + | r1 = ret.Error(1) | |||
| 72 | + | } | |||
| 73 | + | return r0, r1 | |||
| 74 | + | } | |||
| 75 | + | ||||
| 76 | + | // MergeGit_BranchHead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BranchHead' | |||
| 77 | + | type MergeGit_BranchHead_Call struct { | |||
| 78 | + | *mock.Call | |||
| 79 | + | } | |||
| 80 | + | ||||
| 81 | + | // BranchHead is a helper method to define mock.On call | |||
| 82 | + | // - ctx context.Context | |||
| 83 | + | // - repo domain.RepoName | |||
| 84 | + | // - branch string | |||
| 85 | + | func (_e *MergeGit_Expecter) BranchHead(ctx any, repo any, branch any) *MergeGit_BranchHead_Call { | |||
| 86 | + | return &MergeGit_BranchHead_Call{Call: _e.mock.On("BranchHead", ctx, repo, branch)} | |||
| 87 | + | } | |||
| 88 | + | ||||
| 89 | + | func (_c *MergeGit_BranchHead_Call) Run(run func(ctx context.Context, repo domain.RepoName, branch string)) *MergeGit_BranchHead_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.RepoName | |||
| 96 | + | if args[1] != nil { | |||
| 97 | + | arg1 = args[1].(domain.RepoName) | |||
| 98 | + | } | |||
| 99 | + | var arg2 string | |||
| 100 | + | if args[2] != nil { | |||
| 101 | + | arg2 = args[2].(string) | |||
| 102 | + | } | |||
| 103 | + | run( | |||
| 104 | + | arg0, | |||
| 105 | + | arg1, | |||
| 106 | + | arg2, | |||
| 107 | + | ) | |||
| 108 | + | }) | |||
| 109 | + | return _c | |||
| 110 | + | } | |||
| 111 | + | ||||
| 112 | + | func (_c *MergeGit_BranchHead_Call) Return(objectID domain.ObjectID, err error) *MergeGit_BranchHead_Call { | |||
| 113 | + | _c.Call.Return(objectID, err) | |||
| 114 | + | return _c | |||
| 115 | + | } | |||
| 116 | + | ||||
| 117 | + | func (_c *MergeGit_BranchHead_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, branch string) (domain.ObjectID, error)) *MergeGit_BranchHead_Call { | |||
| 118 | + | _c.Call.Return(run) | |||
| 119 | + | return _c | |||
| 120 | + | } | |||
| 121 | + | ||||
| 122 | + | // CommitTree provides a mock function for the type MergeGit | |||
| 123 | + | func (_mock *MergeGit) CommitTree(ctx context.Context, repo domain.RepoName, id domain.ObjectID) (domain.ObjectID, error) { | |||
| 124 | + | ret := _mock.Called(ctx, repo, id) | |||
| 125 | + | ||||
| 126 | + | if len(ret) == 0 { | |||
| 127 | + | panic("no return value specified for CommitTree") | |||
| 128 | + | } | |||
| 129 | + | ||||
| 130 | + | var r0 domain.ObjectID | |||
| 131 | + | var r1 error | |||
| 132 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID) (domain.ObjectID, error)); ok { | |||
| 133 | + | return returnFunc(ctx, repo, id) | |||
| 134 | + | } | |||
| 135 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID) domain.ObjectID); ok { | |||
| 136 | + | r0 = returnFunc(ctx, repo, id) | |||
| 137 | + | } else { | |||
| 138 | + | r0 = ret.Get(0).(domain.ObjectID) | |||
| 139 | + | } | |||
| 140 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, domain.ObjectID) error); ok { | |||
| 141 | + | r1 = returnFunc(ctx, repo, id) | |||
| 142 | + | } else { | |||
| 143 | + | r1 = ret.Error(1) | |||
| 144 | + | } | |||
| 145 | + | return r0, r1 | |||
| 146 | + | } | |||
| 147 | + | ||||
| 148 | + | // MergeGit_CommitTree_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CommitTree' | |||
| 149 | + | type MergeGit_CommitTree_Call struct { | |||
| 150 | + | *mock.Call | |||
| 151 | + | } | |||
| 152 | + | ||||
| 153 | + | // CommitTree is a helper method to define mock.On call | |||
| 154 | + | // - ctx context.Context | |||
| 155 | + | // - repo domain.RepoName | |||
| 156 | + | // - id domain.ObjectID | |||
| 157 | + | func (_e *MergeGit_Expecter) CommitTree(ctx any, repo any, id any) *MergeGit_CommitTree_Call { | |||
| 158 | + | return &MergeGit_CommitTree_Call{Call: _e.mock.On("CommitTree", ctx, repo, id)} | |||
| 159 | + | } | |||
| 160 | + | ||||
| 161 | + | func (_c *MergeGit_CommitTree_Call) Run(run func(ctx context.Context, repo domain.RepoName, id domain.ObjectID)) *MergeGit_CommitTree_Call { | |||
| 162 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 163 | + | var arg0 context.Context | |||
| 164 | + | if args[0] != nil { | |||
| 165 | + | arg0 = args[0].(context.Context) | |||
| 166 | + | } | |||
| 167 | + | var arg1 domain.RepoName | |||
| 168 | + | if args[1] != nil { | |||
| 169 | + | arg1 = args[1].(domain.RepoName) | |||
| 170 | + | } | |||
| 171 | + | var arg2 domain.ObjectID | |||
| 172 | + | if args[2] != nil { | |||
| 173 | + | arg2 = args[2].(domain.ObjectID) | |||
| 174 | + | } | |||
| 175 | + | run( | |||
| 176 | + | arg0, | |||
| 177 | + | arg1, | |||
| 178 | + | arg2, | |||
| 179 | + | ) | |||
| 180 | + | }) | |||
| 181 | + | return _c | |||
| 182 | + | } | |||
| 183 | + | ||||
| 184 | + | func (_c *MergeGit_CommitTree_Call) Return(objectID domain.ObjectID, err error) *MergeGit_CommitTree_Call { | |||
| 185 | + | _c.Call.Return(objectID, err) | |||
| 186 | + | return _c | |||
| 187 | + | } | |||
| 188 | + | ||||
| 189 | + | func (_c *MergeGit_CommitTree_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, id domain.ObjectID) (domain.ObjectID, error)) *MergeGit_CommitTree_Call { | |||
| 190 | + | _c.Call.Return(run) | |||
| 191 | + | return _c | |||
| 192 | + | } | |||
| 193 | + | ||||
| 194 | + | // DiffCommits provides a mock function for the type MergeGit | |||
| 195 | + | func (_mock *MergeGit) DiffCommits(ctx context.Context, repo domain.RepoName, base domain.ObjectID, head domain.ObjectID, limits domain.DiffLimits) (domain.DiffStats, domain.Diff, error) { | |||
| 196 | + | ret := _mock.Called(ctx, repo, base, head, limits) | |||
| 197 | + | ||||
| 198 | + | if len(ret) == 0 { | |||
| 199 | + | panic("no return value specified for DiffCommits") | |||
| 200 | + | } | |||
| 201 | + | ||||
| 202 | + | var r0 domain.DiffStats | |||
| 203 | + | var r1 domain.Diff | |||
| 204 | + | var r2 error | |||
| 205 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID, domain.DiffLimits) (domain.DiffStats, domain.Diff, error)); ok { | |||
| 206 | + | return returnFunc(ctx, repo, base, head, limits) | |||
| 207 | + | } | |||
| 208 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID, domain.DiffLimits) domain.DiffStats); ok { | |||
| 209 | + | r0 = returnFunc(ctx, repo, base, head, limits) | |||
| 210 | + | } else { | |||
| 211 | + | r0 = ret.Get(0).(domain.DiffStats) | |||
| 212 | + | } | |||
| 213 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID, domain.DiffLimits) domain.Diff); ok { | |||
| 214 | + | r1 = returnFunc(ctx, repo, base, head, limits) | |||
| 215 | + | } else { | |||
| 216 | + | r1 = ret.Get(1).(domain.Diff) | |||
| 217 | + | } | |||
| 218 | + | if returnFunc, ok := ret.Get(2).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID, domain.DiffLimits) error); ok { | |||
| 219 | + | r2 = returnFunc(ctx, repo, base, head, limits) | |||
| 220 | + | } else { | |||
| 221 | + | r2 = ret.Error(2) | |||
| 222 | + | } | |||
| 223 | + | return r0, r1, r2 | |||
| 224 | + | } | |||
| 225 | + | ||||
| 226 | + | // MergeGit_DiffCommits_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DiffCommits' | |||
| 227 | + | type MergeGit_DiffCommits_Call struct { | |||
| 228 | + | *mock.Call | |||
| 229 | + | } | |||
| 230 | + | ||||
| 231 | + | // DiffCommits is a helper method to define mock.On call | |||
| 232 | + | // - ctx context.Context | |||
| 233 | + | // - repo domain.RepoName | |||
| 234 | + | // - base domain.ObjectID | |||
| 235 | + | // - head domain.ObjectID | |||
| 236 | + | // - limits domain.DiffLimits | |||
| 237 | + | func (_e *MergeGit_Expecter) DiffCommits(ctx any, repo any, base any, head any, limits any) *MergeGit_DiffCommits_Call { | |||
| 238 | + | return &MergeGit_DiffCommits_Call{Call: _e.mock.On("DiffCommits", ctx, repo, base, head, limits)} | |||
| 239 | + | } | |||
| 240 | + | ||||
| 241 | + | func (_c *MergeGit_DiffCommits_Call) Run(run func(ctx context.Context, repo domain.RepoName, base domain.ObjectID, head domain.ObjectID, limits domain.DiffLimits)) *MergeGit_DiffCommits_Call { | |||
| 242 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 243 | + | var arg0 context.Context | |||
| 244 | + | if args[0] != nil { | |||
| 245 | + | arg0 = args[0].(context.Context) | |||
| 246 | + | } | |||
| 247 | + | var arg1 domain.RepoName | |||
| 248 | + | if args[1] != nil { | |||
| 249 | + | arg1 = args[1].(domain.RepoName) | |||
| 250 | + | } | |||
| 251 | + | var arg2 domain.ObjectID | |||
| 252 | + | if args[2] != nil { | |||
| 253 | + | arg2 = args[2].(domain.ObjectID) | |||
| 254 | + | } | |||
| 255 | + | var arg3 domain.ObjectID | |||
| 256 | + | if args[3] != nil { | |||
| 257 | + | arg3 = args[3].(domain.ObjectID) | |||
| 258 | + | } | |||
| 259 | + | var arg4 domain.DiffLimits | |||
| 260 | + | if args[4] != nil { | |||
| 261 | + | arg4 = args[4].(domain.DiffLimits) | |||
| 262 | + | } | |||
| 263 | + | run( | |||
| 264 | + | arg0, | |||
| 265 | + | arg1, | |||
| 266 | + | arg2, | |||
| 267 | + | arg3, | |||
| 268 | + | arg4, | |||
| 269 | + | ) | |||
| 270 | + | }) | |||
| 271 | + | return _c | |||
| 272 | + | } | |||
| 273 | + | ||||
| 274 | + | func (_c *MergeGit_DiffCommits_Call) Return(diffStats domain.DiffStats, diff domain.Diff, err error) *MergeGit_DiffCommits_Call { | |||
| 275 | + | _c.Call.Return(diffStats, diff, err) | |||
| 276 | + | return _c | |||
| 277 | + | } | |||
| 278 | + | ||||
| 279 | + | func (_c *MergeGit_DiffCommits_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, base domain.ObjectID, head domain.ObjectID, limits domain.DiffLimits) (domain.DiffStats, domain.Diff, error)) *MergeGit_DiffCommits_Call { | |||
| 280 | + | _c.Call.Return(run) | |||
| 281 | + | return _c | |||
| 282 | + | } | |||
| 283 | + | ||||
| 284 | + | // HasCommit provides a mock function for the type MergeGit | |||
| 285 | + | func (_mock *MergeGit) HasCommit(ctx context.Context, repo domain.RepoName, id domain.ObjectID) (bool, error) { | |||
| 286 | + | ret := _mock.Called(ctx, repo, id) | |||
| 287 | + | ||||
| 288 | + | if len(ret) == 0 { | |||
| 289 | + | panic("no return value specified for HasCommit") | |||
| 290 | + | } | |||
| 291 | + | ||||
| 292 | + | var r0 bool | |||
| 293 | + | var r1 error | |||
| 294 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID) (bool, error)); ok { | |||
| 295 | + | return returnFunc(ctx, repo, id) | |||
| 296 | + | } | |||
| 297 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID) bool); ok { | |||
| 298 | + | r0 = returnFunc(ctx, repo, id) | |||
| 299 | + | } else { | |||
| 300 | + | r0 = ret.Get(0).(bool) | |||
| 301 | + | } | |||
| 302 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, domain.ObjectID) error); ok { | |||
| 303 | + | r1 = returnFunc(ctx, repo, id) | |||
| 304 | + | } else { | |||
| 305 | + | r1 = ret.Error(1) | |||
| 306 | + | } | |||
| 307 | + | return r0, r1 | |||
| 308 | + | } | |||
| 309 | + | ||||
| 310 | + | // MergeGit_HasCommit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HasCommit' | |||
| 311 | + | type MergeGit_HasCommit_Call struct { | |||
| 312 | + | *mock.Call | |||
| 313 | + | } | |||
| 314 | + | ||||
| 315 | + | // HasCommit is a helper method to define mock.On call | |||
| 316 | + | // - ctx context.Context | |||
| 317 | + | // - repo domain.RepoName | |||
| 318 | + | // - id domain.ObjectID | |||
| 319 | + | func (_e *MergeGit_Expecter) HasCommit(ctx any, repo any, id any) *MergeGit_HasCommit_Call { | |||
| 320 | + | return &MergeGit_HasCommit_Call{Call: _e.mock.On("HasCommit", ctx, repo, id)} | |||
| 321 | + | } | |||
| 322 | + | ||||
| 323 | + | func (_c *MergeGit_HasCommit_Call) Run(run func(ctx context.Context, repo domain.RepoName, id domain.ObjectID)) *MergeGit_HasCommit_Call { | |||
| 324 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 325 | + | var arg0 context.Context | |||
| 326 | + | if args[0] != nil { | |||
| 327 | + | arg0 = args[0].(context.Context) | |||
| 328 | + | } | |||
| 329 | + | var arg1 domain.RepoName | |||
| 330 | + | if args[1] != nil { | |||
| 331 | + | arg1 = args[1].(domain.RepoName) | |||
| 332 | + | } | |||
| 333 | + | var arg2 domain.ObjectID | |||
| 334 | + | if args[2] != nil { | |||
| 335 | + | arg2 = args[2].(domain.ObjectID) | |||
| 336 | + | } | |||
| 337 | + | run( | |||
| 338 | + | arg0, | |||
| 339 | + | arg1, | |||
| 340 | + | arg2, | |||
| 341 | + | ) | |||
| 342 | + | }) | |||
| 343 | + | return _c | |||
| 344 | + | } | |||
| 345 | + | ||||
| 346 | + | func (_c *MergeGit_HasCommit_Call) Return(b bool, err error) *MergeGit_HasCommit_Call { | |||
| 347 | + | _c.Call.Return(b, err) | |||
| 348 | + | return _c | |||
| 349 | + | } | |||
| 350 | + | ||||
| 351 | + | func (_c *MergeGit_HasCommit_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, id domain.ObjectID) (bool, error)) *MergeGit_HasCommit_Call { | |||
| 352 | + | _c.Call.Return(run) | |||
| 353 | + | return _c | |||
| 354 | + | } | |||
| 355 | + | ||||
| 356 | + | // IsAncestor provides a mock function for the type MergeGit | |||
| 357 | + | func (_mock *MergeGit) IsAncestor(ctx context.Context, repo domain.RepoName, ancestor domain.ObjectID, descendant domain.ObjectID) (bool, error) { | |||
| 358 | + | ret := _mock.Called(ctx, repo, ancestor, descendant) | |||
| 359 | + | ||||
| 360 | + | if len(ret) == 0 { | |||
| 361 | + | panic("no return value specified for IsAncestor") | |||
| 362 | + | } | |||
| 363 | + | ||||
| 364 | + | var r0 bool | |||
| 365 | + | var r1 error | |||
| 366 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID) (bool, error)); ok { | |||
| 367 | + | return returnFunc(ctx, repo, ancestor, descendant) | |||
| 368 | + | } | |||
| 369 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID) bool); ok { | |||
| 370 | + | r0 = returnFunc(ctx, repo, ancestor, descendant) | |||
| 371 | + | } else { | |||
| 372 | + | r0 = ret.Get(0).(bool) | |||
| 373 | + | } | |||
| 374 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID) error); ok { | |||
| 375 | + | r1 = returnFunc(ctx, repo, ancestor, descendant) | |||
| 376 | + | } else { | |||
| 377 | + | r1 = ret.Error(1) | |||
| 378 | + | } | |||
| 379 | + | return r0, r1 | |||
| 380 | + | } | |||
| 381 | + | ||||
| 382 | + | // MergeGit_IsAncestor_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsAncestor' | |||
| 383 | + | type MergeGit_IsAncestor_Call struct { | |||
| 384 | + | *mock.Call | |||
| 385 | + | } | |||
| 386 | + | ||||
| 387 | + | // IsAncestor is a helper method to define mock.On call | |||
| 388 | + | // - ctx context.Context | |||
| 389 | + | // - repo domain.RepoName | |||
| 390 | + | // - ancestor domain.ObjectID | |||
| 391 | + | // - descendant domain.ObjectID | |||
| 392 | + | func (_e *MergeGit_Expecter) IsAncestor(ctx any, repo any, ancestor any, descendant any) *MergeGit_IsAncestor_Call { | |||
| 393 | + | return &MergeGit_IsAncestor_Call{Call: _e.mock.On("IsAncestor", ctx, repo, ancestor, descendant)} | |||
| 394 | + | } | |||
| 395 | + | ||||
| 396 | + | func (_c *MergeGit_IsAncestor_Call) Run(run func(ctx context.Context, repo domain.RepoName, ancestor domain.ObjectID, descendant domain.ObjectID)) *MergeGit_IsAncestor_Call { | |||
| 397 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 398 | + | var arg0 context.Context | |||
| 399 | + | if args[0] != nil { | |||
| 400 | + | arg0 = args[0].(context.Context) | |||
| 401 | + | } | |||
| 402 | + | var arg1 domain.RepoName | |||
| 403 | + | if args[1] != nil { | |||
| 404 | + | arg1 = args[1].(domain.RepoName) | |||
| 405 | + | } | |||
| 406 | + | var arg2 domain.ObjectID | |||
| 407 | + | if args[2] != nil { | |||
| 408 | + | arg2 = args[2].(domain.ObjectID) | |||
| 409 | + | } | |||
| 410 | + | var arg3 domain.ObjectID | |||
| 411 | + | if args[3] != nil { | |||
| 412 | + | arg3 = args[3].(domain.ObjectID) | |||
| 413 | + | } | |||
| 414 | + | run( | |||
| 415 | + | arg0, | |||
| 416 | + | arg1, | |||
| 417 | + | arg2, | |||
| 418 | + | arg3, | |||
| 419 | + | ) | |||
| 420 | + | }) | |||
| 421 | + | return _c | |||
| 422 | + | } | |||
| 423 | + | ||||
| 424 | + | func (_c *MergeGit_IsAncestor_Call) Return(b bool, err error) *MergeGit_IsAncestor_Call { | |||
| 425 | + | _c.Call.Return(b, err) | |||
| 426 | + | return _c | |||
| 427 | + | } | |||
| 428 | + | ||||
| 429 | + | func (_c *MergeGit_IsAncestor_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, ancestor domain.ObjectID, descendant domain.ObjectID) (bool, error)) *MergeGit_IsAncestor_Call { | |||
| 430 | + | _c.Call.Return(run) | |||
| 431 | + | return _c | |||
| 432 | + | } | |||
| 433 | + | ||||
| 434 | + | // ListRange provides a mock function for the type MergeGit | |||
| 435 | + | func (_mock *MergeGit) ListRange(ctx context.Context, repo domain.RepoName, exclude domain.ObjectID, head domain.ObjectID, limit int) ([]domain.Commit, bool, error) { | |||
| 436 | + | ret := _mock.Called(ctx, repo, exclude, head, limit) | |||
| 437 | + | ||||
| 438 | + | if len(ret) == 0 { | |||
| 439 | + | panic("no return value specified for ListRange") | |||
| 440 | + | } | |||
| 441 | + | ||||
| 442 | + | var r0 []domain.Commit | |||
| 443 | + | var r1 bool | |||
| 444 | + | var r2 error | |||
| 445 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID, int) ([]domain.Commit, bool, error)); ok { | |||
| 446 | + | return returnFunc(ctx, repo, exclude, head, limit) | |||
| 447 | + | } | |||
| 448 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID, int) []domain.Commit); ok { | |||
| 449 | + | r0 = returnFunc(ctx, repo, exclude, head, limit) | |||
| 450 | + | } else { | |||
| 451 | + | if ret.Get(0) != nil { | |||
| 452 | + | r0 = ret.Get(0).([]domain.Commit) | |||
| 453 | + | } | |||
| 454 | + | } | |||
| 455 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID, int) bool); ok { | |||
| 456 | + | r1 = returnFunc(ctx, repo, exclude, head, limit) | |||
| 457 | + | } else { | |||
| 458 | + | r1 = ret.Get(1).(bool) | |||
| 459 | + | } | |||
| 460 | + | if returnFunc, ok := ret.Get(2).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID, int) error); ok { | |||
| 461 | + | r2 = returnFunc(ctx, repo, exclude, head, limit) | |||
| 462 | + | } else { | |||
| 463 | + | r2 = ret.Error(2) | |||
| 464 | + | } | |||
| 465 | + | return r0, r1, r2 | |||
| 466 | + | } | |||
| 467 | + | ||||
| 468 | + | // MergeGit_ListRange_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListRange' | |||
| 469 | + | type MergeGit_ListRange_Call struct { | |||
| 470 | + | *mock.Call | |||
| 471 | + | } | |||
| 472 | + | ||||
| 473 | + | // ListRange is a helper method to define mock.On call | |||
| 474 | + | // - ctx context.Context | |||
| 475 | + | // - repo domain.RepoName | |||
| 476 | + | // - exclude domain.ObjectID | |||
| 477 | + | // - head domain.ObjectID | |||
| 478 | + | // - limit int | |||
| 479 | + | func (_e *MergeGit_Expecter) ListRange(ctx any, repo any, exclude any, head any, limit any) *MergeGit_ListRange_Call { | |||
| 480 | + | return &MergeGit_ListRange_Call{Call: _e.mock.On("ListRange", ctx, repo, exclude, head, limit)} | |||
| 481 | + | } | |||
| 482 | + | ||||
| 483 | + | func (_c *MergeGit_ListRange_Call) Run(run func(ctx context.Context, repo domain.RepoName, exclude domain.ObjectID, head domain.ObjectID, limit int)) *MergeGit_ListRange_Call { | |||
| 484 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 485 | + | var arg0 context.Context | |||
| 486 | + | if args[0] != nil { | |||
| 487 | + | arg0 = args[0].(context.Context) | |||
| 488 | + | } | |||
| 489 | + | var arg1 domain.RepoName | |||
| 490 | + | if args[1] != nil { | |||
| 491 | + | arg1 = args[1].(domain.RepoName) | |||
| 492 | + | } | |||
| 493 | + | var arg2 domain.ObjectID | |||
| 494 | + | if args[2] != nil { | |||
| 495 | + | arg2 = args[2].(domain.ObjectID) | |||
| 496 | + | } | |||
| 497 | + | var arg3 domain.ObjectID | |||
| 498 | + | if args[3] != nil { | |||
| 499 | + | arg3 = args[3].(domain.ObjectID) | |||
| 500 | + | } | |||
| 501 | + | var arg4 int | |||
| 502 | + | if args[4] != nil { | |||
| 503 | + | arg4 = args[4].(int) | |||
| 504 | + | } | |||
| 505 | + | run( | |||
| 506 | + | arg0, | |||
| 507 | + | arg1, | |||
| 508 | + | arg2, | |||
| 509 | + | arg3, | |||
| 510 | + | arg4, | |||
| 511 | + | ) | |||
| 512 | + | }) | |||
| 513 | + | return _c | |||
| 514 | + | } | |||
| 515 | + | ||||
| 516 | + | func (_c *MergeGit_ListRange_Call) Return(commits []domain.Commit, more bool, err error) *MergeGit_ListRange_Call { | |||
| 517 | + | _c.Call.Return(commits, more, err) | |||
| 518 | + | return _c | |||
| 519 | + | } | |||
| 520 | + | ||||
| 521 | + | func (_c *MergeGit_ListRange_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, exclude domain.ObjectID, head domain.ObjectID, limit int) ([]domain.Commit, bool, error)) *MergeGit_ListRange_Call { | |||
| 522 | + | _c.Call.Return(run) | |||
| 523 | + | return _c | |||
| 524 | + | } | |||
| 525 | + | ||||
| 526 | + | // MergeBase provides a mock function for the type MergeGit | |||
| 527 | + | func (_mock *MergeGit) MergeBase(ctx context.Context, repo domain.RepoName, a domain.ObjectID, b domain.ObjectID) (domain.ObjectID, error) { | |||
| 528 | + | ret := _mock.Called(ctx, repo, a, b) | |||
| 529 | + | ||||
| 530 | + | if len(ret) == 0 { | |||
| 531 | + | panic("no return value specified for MergeBase") | |||
| 532 | + | } | |||
| 533 | + | ||||
| 534 | + | var r0 domain.ObjectID | |||
| 535 | + | var r1 error | |||
| 536 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID) (domain.ObjectID, error)); ok { | |||
| 537 | + | return returnFunc(ctx, repo, a, b) | |||
| 538 | + | } | |||
| 539 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID) domain.ObjectID); ok { | |||
| 540 | + | r0 = returnFunc(ctx, repo, a, b) | |||
| 541 | + | } else { | |||
| 542 | + | r0 = ret.Get(0).(domain.ObjectID) | |||
| 543 | + | } | |||
| 544 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, domain.ObjectID, domain.ObjectID) error); ok { | |||
| 545 | + | r1 = returnFunc(ctx, repo, a, b) | |||
| 546 | + | } else { | |||
| 547 | + | r1 = ret.Error(1) | |||
| 548 | + | } | |||
| 549 | + | return r0, r1 | |||
| 550 | + | } | |||
| 551 | + | ||||
| 552 | + | // MergeGit_MergeBase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MergeBase' | |||
| 553 | + | type MergeGit_MergeBase_Call struct { | |||
| 554 | + | *mock.Call | |||
| 555 | + | } | |||
| 556 | + | ||||
| 557 | + | // MergeBase is a helper method to define mock.On call | |||
| 558 | + | // - ctx context.Context | |||
| 559 | + | // - repo domain.RepoName | |||
| 560 | + | // - a domain.ObjectID | |||
| 561 | + | // - b domain.ObjectID | |||
| 562 | + | func (_e *MergeGit_Expecter) MergeBase(ctx any, repo any, a any, b any) *MergeGit_MergeBase_Call { | |||
| 563 | + | return &MergeGit_MergeBase_Call{Call: _e.mock.On("MergeBase", ctx, repo, a, b)} | |||
| 564 | + | } | |||
| 565 | + | ||||
| 566 | + | func (_c *MergeGit_MergeBase_Call) Run(run func(ctx context.Context, repo domain.RepoName, a domain.ObjectID, b domain.ObjectID)) *MergeGit_MergeBase_Call { | |||
| 567 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 568 | + | var arg0 context.Context | |||
| 569 | + | if args[0] != nil { | |||
| 570 | + | arg0 = args[0].(context.Context) | |||
| 571 | + | } | |||
| 572 | + | var arg1 domain.RepoName | |||
| 573 | + | if args[1] != nil { | |||
| 574 | + | arg1 = args[1].(domain.RepoName) | |||
| 575 | + | } | |||
| 576 | + | var arg2 domain.ObjectID | |||
| 577 | + | if args[2] != nil { | |||
| 578 | + | arg2 = args[2].(domain.ObjectID) | |||
| 579 | + | } | |||
| 580 | + | var arg3 domain.ObjectID | |||
| 581 | + | if args[3] != nil { | |||
| 582 | + | arg3 = args[3].(domain.ObjectID) | |||
| 583 | + | } | |||
| 584 | + | run( | |||
| 585 | + | arg0, | |||
| 586 | + | arg1, | |||
| 587 | + | arg2, | |||
| 588 | + | arg3, | |||
| 589 | + | ) | |||
| 590 | + | }) | |||
| 591 | + | return _c | |||
| 592 | + | } | |||
| 593 | + | ||||
| 594 | + | func (_c *MergeGit_MergeBase_Call) Return(objectID domain.ObjectID, err error) *MergeGit_MergeBase_Call { | |||
| 595 | + | _c.Call.Return(objectID, err) | |||
| 596 | + | return _c | |||
| 597 | + | } | |||
| 598 | + | ||||
| 599 | + | func (_c *MergeGit_MergeBase_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, a domain.ObjectID, b domain.ObjectID) (domain.ObjectID, error)) *MergeGit_MergeBase_Call { | |||
| 600 | + | _c.Call.Return(run) | |||
| 601 | + | return _c | |||
| 602 | + | } | |||
| 603 | + | ||||
| 604 | + | // UpdateBranch provides a mock function for the type MergeGit | |||
| 605 | + | func (_mock *MergeGit) UpdateBranch(ctx context.Context, repo domain.RepoName, branch string, old domain.ObjectID, new domain.ObjectID) error { | |||
| 606 | + | ret := _mock.Called(ctx, repo, branch, old, new) | |||
| 607 | + | ||||
| 608 | + | if len(ret) == 0 { | |||
| 609 | + | panic("no return value specified for UpdateBranch") | |||
| 610 | + | } | |||
| 611 | + | ||||
| 612 | + | var r0 error | |||
| 613 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, string, domain.ObjectID, domain.ObjectID) error); ok { | |||
| 614 | + | r0 = returnFunc(ctx, repo, branch, old, new) | |||
| 615 | + | } else { | |||
| 616 | + | r0 = ret.Error(0) | |||
| 617 | + | } | |||
| 618 | + | return r0 | |||
| 619 | + | } | |||
| 620 | + | ||||
| 621 | + | // MergeGit_UpdateBranch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateBranch' | |||
| 622 | + | type MergeGit_UpdateBranch_Call struct { | |||
| 623 | + | *mock.Call | |||
| 624 | + | } | |||
| 625 | + | ||||
| 626 | + | // UpdateBranch is a helper method to define mock.On call | |||
| 627 | + | // - ctx context.Context | |||
| 628 | + | // - repo domain.RepoName | |||
| 629 | + | // - branch string | |||
| 630 | + | // - old domain.ObjectID | |||
| 631 | + | // - new domain.ObjectID | |||
| 632 | + | func (_e *MergeGit_Expecter) UpdateBranch(ctx any, repo any, branch any, old any, new any) *MergeGit_UpdateBranch_Call { | |||
| 633 | + | return &MergeGit_UpdateBranch_Call{Call: _e.mock.On("UpdateBranch", ctx, repo, branch, old, new)} | |||
| 634 | + | } | |||
| 635 | + | ||||
| 636 | + | func (_c *MergeGit_UpdateBranch_Call) Run(run func(ctx context.Context, repo domain.RepoName, branch string, old domain.ObjectID, new domain.ObjectID)) *MergeGit_UpdateBranch_Call { | |||
| 637 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 638 | + | var arg0 context.Context | |||
| 639 | + | if args[0] != nil { | |||
| 640 | + | arg0 = args[0].(context.Context) | |||
| 641 | + | } | |||
| 642 | + | var arg1 domain.RepoName | |||
| 643 | + | if args[1] != nil { | |||
| 644 | + | arg1 = args[1].(domain.RepoName) | |||
| 645 | + | } | |||
| 646 | + | var arg2 string | |||
| 647 | + | if args[2] != nil { | |||
| 648 | + | arg2 = args[2].(string) | |||
| 649 | + | } | |||
| 650 | + | var arg3 domain.ObjectID | |||
| 651 | + | if args[3] != nil { | |||
| 652 | + | arg3 = args[3].(domain.ObjectID) | |||
| 653 | + | } | |||
| 654 | + | var arg4 domain.ObjectID | |||
| 655 | + | if args[4] != nil { | |||
| 656 | + | arg4 = args[4].(domain.ObjectID) | |||
| 657 | + | } | |||
| 658 | + | run( | |||
| 659 | + | arg0, | |||
| 660 | + | arg1, | |||
| 661 | + | arg2, | |||
| 662 | + | arg3, | |||
| 663 | + | arg4, | |||
| 664 | + | ) | |||
| 665 | + | }) | |||
| 666 | + | return _c | |||
| 667 | + | } | |||
| 668 | + | ||||
| 669 | + | func (_c *MergeGit_UpdateBranch_Call) Return(err error) *MergeGit_UpdateBranch_Call { | |||
| 670 | + | _c.Call.Return(err) | |||
| 671 | + | return _c | |||
| 672 | + | } | |||
| 673 | + | ||||
| 674 | + | func (_c *MergeGit_UpdateBranch_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, branch string, old domain.ObjectID, new domain.ObjectID) error) *MergeGit_UpdateBranch_Call { | |||
| 675 | + | _c.Call.Return(run) | |||
| 676 | + | return _c | |||
| 677 | + | } | |||
| 678 | + | ||||
| 679 | + | // WriteCommit provides a mock function for the type MergeGit | |||
| 680 | + | func (_mock *MergeGit) WriteCommit(ctx context.Context, repo domain.RepoName, object []byte) (domain.ObjectID, error) { | |||
| 681 | + | ret := _mock.Called(ctx, repo, object) | |||
| 682 | + | ||||
| 683 | + | if len(ret) == 0 { | |||
| 684 | + | panic("no return value specified for WriteCommit") | |||
| 685 | + | } | |||
| 686 | + | ||||
| 687 | + | var r0 domain.ObjectID | |||
| 688 | + | var r1 error | |||
| 689 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, []byte) (domain.ObjectID, error)); ok { | |||
| 690 | + | return returnFunc(ctx, repo, object) | |||
| 691 | + | } | |||
| 692 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, []byte) domain.ObjectID); ok { | |||
| 693 | + | r0 = returnFunc(ctx, repo, object) | |||
| 694 | + | } else { | |||
| 695 | + | r0 = ret.Get(0).(domain.ObjectID) | |||
| 696 | + | } | |||
| 697 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, []byte) error); ok { | |||
| 698 | + | r1 = returnFunc(ctx, repo, object) | |||
| 699 | + | } else { | |||
| 700 | + | r1 = ret.Error(1) | |||
| 701 | + | } | |||
| 702 | + | return r0, r1 | |||
| 703 | + | } | |||
| 704 | + | ||||
| 705 | + | // MergeGit_WriteCommit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteCommit' | |||
| 706 | + | type MergeGit_WriteCommit_Call struct { | |||
| 707 | + | *mock.Call | |||
| 708 | + | } | |||
| 709 | + | ||||
| 710 | + | // WriteCommit is a helper method to define mock.On call | |||
| 711 | + | // - ctx context.Context | |||
| 712 | + | // - repo domain.RepoName | |||
| 713 | + | // - object []byte | |||
| 714 | + | func (_e *MergeGit_Expecter) WriteCommit(ctx any, repo any, object any) *MergeGit_WriteCommit_Call { | |||
| 715 | + | return &MergeGit_WriteCommit_Call{Call: _e.mock.On("WriteCommit", ctx, repo, object)} | |||
| 716 | + | } | |||
| 717 | + | ||||
| 718 | + | func (_c *MergeGit_WriteCommit_Call) Run(run func(ctx context.Context, repo domain.RepoName, object []byte)) *MergeGit_WriteCommit_Call { | |||
| 719 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 720 | + | var arg0 context.Context | |||
| 721 | + | if args[0] != nil { | |||
| 722 | + | arg0 = args[0].(context.Context) | |||
| 723 | + | } | |||
| 724 | + | var arg1 domain.RepoName | |||
| 725 | + | if args[1] != nil { | |||
| 726 | + | arg1 = args[1].(domain.RepoName) | |||
| 727 | + | } | |||
| 728 | + | var arg2 []byte | |||
| 729 | + | if args[2] != nil { | |||
| 730 | + | arg2 = args[2].([]byte) | |||
| 731 | + | } | |||
| 732 | + | run( | |||
| 733 | + | arg0, | |||
| 734 | + | arg1, | |||
| 735 | + | arg2, | |||
| 736 | + | ) | |||
| 737 | + | }) | |||
| 738 | + | return _c | |||
| 739 | + | } | |||
| 740 | + | ||||
| 741 | + | func (_c *MergeGit_WriteCommit_Call) Return(objectID domain.ObjectID, err error) *MergeGit_WriteCommit_Call { | |||
| 742 | + | _c.Call.Return(objectID, err) | |||
| 743 | + | return _c | |||
| 744 | + | } | |||
| 745 | + | ||||
| 746 | + | func (_c *MergeGit_WriteCommit_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, object []byte) (domain.ObjectID, error)) *MergeGit_WriteCommit_Call { | |||
| 747 | + | _c.Call.Return(run) | |||
| 748 | + | return _c | |||
| 749 | + | } | |||
A internal/ports/mocks/MergeRequestStore.go +667 -0
| @@ -0,0 +1,667 @@ | |||
| 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 | + | // NewMergeRequestStore creates a new instance of MergeRequestStore. 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 NewMergeRequestStore(t interface { | |
| 17 | + | mock.TestingT | |
| 18 | + | Cleanup(func()) | |
| 19 | + | }) *MergeRequestStore { | |
| 20 | + | if helper, ok := t.(interface{ Helper() }); ok { | |
| 21 | + | helper.Helper() | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | mock := &MergeRequestStore{} | |
| 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 | + | // MergeRequestStore is an autogenerated mock type for the MergeRequestStore type | |
| 38 | + | type MergeRequestStore struct { | |
| 39 | + | mock.Mock | |
| 40 | + | } | |
| 41 | + | ||
| 42 | + | type MergeRequestStore_Expecter struct { | |
| 43 | + | mock *mock.Mock | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | func (_m *MergeRequestStore) EXPECT() *MergeRequestStore_Expecter { | |
| 47 | + | return &MergeRequestStore_Expecter{mock: &_m.Mock} | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | // Create provides a mock function for the type MergeRequestStore | |
| 51 | + | func (_mock *MergeRequestStore) Create(ctx context.Context, mr domain.MergeRequest) (domain.MergeRequest, error) { | |
| 52 | + | ret := _mock.Called(ctx, mr) | |
| 53 | + | ||
| 54 | + | if len(ret) == 0 { | |
| 55 | + | panic("no return value specified for Create") | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | var r0 domain.MergeRequest | |
| 59 | + | var r1 error | |
| 60 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.MergeRequest) (domain.MergeRequest, error)); ok { | |
| 61 | + | return returnFunc(ctx, mr) | |
| 62 | + | } | |
| 63 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.MergeRequest) domain.MergeRequest); ok { | |
| 64 | + | r0 = returnFunc(ctx, mr) | |
| 65 | + | } else { | |
| 66 | + | r0 = ret.Get(0).(domain.MergeRequest) | |
| 67 | + | } | |
| 68 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.MergeRequest) error); ok { | |
| 69 | + | r1 = returnFunc(ctx, mr) | |
| 70 | + | } else { | |
| 71 | + | r1 = ret.Error(1) | |
| 72 | + | } | |
| 73 | + | return r0, r1 | |
| 74 | + | } | |
| 75 | + | ||
| 76 | + | // MergeRequestStore_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create' | |
| 77 | + | type MergeRequestStore_Create_Call struct { | |
| 78 | + | *mock.Call | |
| 79 | + | } | |
| 80 | + | ||
| 81 | + | // Create is a helper method to define mock.On call | |
| 82 | + | // - ctx context.Context | |
| 83 | + | // - mr domain.MergeRequest | |
| 84 | + | func (_e *MergeRequestStore_Expecter) Create(ctx any, mr any) *MergeRequestStore_Create_Call { | |
| 85 | + | return &MergeRequestStore_Create_Call{Call: _e.mock.On("Create", ctx, mr)} | |
| 86 | + | } | |
| 87 | + | ||
| 88 | + | func (_c *MergeRequestStore_Create_Call) Run(run func(ctx context.Context, mr domain.MergeRequest)) *MergeRequestStore_Create_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 domain.MergeRequest | |
| 95 | + | if args[1] != nil { | |
| 96 | + | arg1 = args[1].(domain.MergeRequest) | |
| 97 | + | } | |
| 98 | + | run( | |
| 99 | + | arg0, | |
| 100 | + | arg1, | |
| 101 | + | ) | |
| 102 | + | }) | |
| 103 | + | return _c | |
| 104 | + | } | |
| 105 | + | ||
| 106 | + | func (_c *MergeRequestStore_Create_Call) Return(mergeRequest domain.MergeRequest, err error) *MergeRequestStore_Create_Call { | |
| 107 | + | _c.Call.Return(mergeRequest, err) | |
| 108 | + | return _c | |
| 109 | + | } | |
| 110 | + | ||
| 111 | + | func (_c *MergeRequestStore_Create_Call) RunAndReturn(run func(ctx context.Context, mr domain.MergeRequest) (domain.MergeRequest, error)) *MergeRequestStore_Create_Call { | |
| 112 | + | _c.Call.Return(run) | |
| 113 | + | return _c | |
| 114 | + | } | |
| 115 | + | ||
| 116 | + | // Get provides a mock function for the type MergeRequestStore | |
| 117 | + | func (_mock *MergeRequestStore) Get(ctx context.Context, repo domain.RepoName, number int64) (domain.MergeRequest, error) { | |
| 118 | + | ret := _mock.Called(ctx, repo, number) | |
| 119 | + | ||
| 120 | + | if len(ret) == 0 { | |
| 121 | + | panic("no return value specified for Get") | |
| 122 | + | } | |
| 123 | + | ||
| 124 | + | var r0 domain.MergeRequest | |
| 125 | + | var r1 error | |
| 126 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64) (domain.MergeRequest, error)); ok { | |
| 127 | + | return returnFunc(ctx, repo, number) | |
| 128 | + | } | |
| 129 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64) domain.MergeRequest); ok { | |
| 130 | + | r0 = returnFunc(ctx, repo, number) | |
| 131 | + | } else { | |
| 132 | + | r0 = ret.Get(0).(domain.MergeRequest) | |
| 133 | + | } | |
| 134 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, int64) error); ok { | |
| 135 | + | r1 = returnFunc(ctx, repo, number) | |
| 136 | + | } else { | |
| 137 | + | r1 = ret.Error(1) | |
| 138 | + | } | |
| 139 | + | return r0, r1 | |
| 140 | + | } | |
| 141 | + | ||
| 142 | + | // MergeRequestStore_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' | |
| 143 | + | type MergeRequestStore_Get_Call struct { | |
| 144 | + | *mock.Call | |
| 145 | + | } | |
| 146 | + | ||
| 147 | + | // Get is a helper method to define mock.On call | |
| 148 | + | // - ctx context.Context | |
| 149 | + | // - repo domain.RepoName | |
| 150 | + | // - number int64 | |
| 151 | + | func (_e *MergeRequestStore_Expecter) Get(ctx any, repo any, number any) *MergeRequestStore_Get_Call { | |
| 152 | + | return &MergeRequestStore_Get_Call{Call: _e.mock.On("Get", ctx, repo, number)} | |
| 153 | + | } | |
| 154 | + | ||
| 155 | + | func (_c *MergeRequestStore_Get_Call) Run(run func(ctx context.Context, repo domain.RepoName, number int64)) *MergeRequestStore_Get_Call { | |
| 156 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 157 | + | var arg0 context.Context | |
| 158 | + | if args[0] != nil { | |
| 159 | + | arg0 = args[0].(context.Context) | |
| 160 | + | } | |
| 161 | + | var arg1 domain.RepoName | |
| 162 | + | if args[1] != nil { | |
| 163 | + | arg1 = args[1].(domain.RepoName) | |
| 164 | + | } | |
| 165 | + | var arg2 int64 | |
| 166 | + | if args[2] != nil { | |
| 167 | + | arg2 = args[2].(int64) | |
| 168 | + | } | |
| 169 | + | run( | |
| 170 | + | arg0, | |
| 171 | + | arg1, | |
| 172 | + | arg2, | |
| 173 | + | ) | |
| 174 | + | }) | |
| 175 | + | return _c | |
| 176 | + | } | |
| 177 | + | ||
| 178 | + | func (_c *MergeRequestStore_Get_Call) Return(mergeRequest domain.MergeRequest, err error) *MergeRequestStore_Get_Call { | |
| 179 | + | _c.Call.Return(mergeRequest, err) | |
| 180 | + | return _c | |
| 181 | + | } | |
| 182 | + | ||
| 183 | + | func (_c *MergeRequestStore_Get_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, number int64) (domain.MergeRequest, error)) *MergeRequestStore_Get_Call { | |
| 184 | + | _c.Call.Return(run) | |
| 185 | + | return _c | |
| 186 | + | } | |
| 187 | + | ||
| 188 | + | // GetComment provides a mock function for the type MergeRequestStore | |
| 189 | + | func (_mock *MergeRequestStore) GetComment(ctx context.Context, repo domain.RepoName, number int64, id int64) (domain.Comment, error) { | |
| 190 | + | ret := _mock.Called(ctx, repo, number, id) | |
| 191 | + | ||
| 192 | + | if len(ret) == 0 { | |
| 193 | + | panic("no return value specified for GetComment") | |
| 194 | + | } | |
| 195 | + | ||
| 196 | + | var r0 domain.Comment | |
| 197 | + | var r1 error | |
| 198 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64, int64) (domain.Comment, error)); ok { | |
| 199 | + | return returnFunc(ctx, repo, number, id) | |
| 200 | + | } | |
| 201 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64, int64) domain.Comment); ok { | |
| 202 | + | r0 = returnFunc(ctx, repo, number, id) | |
| 203 | + | } else { | |
| 204 | + | r0 = ret.Get(0).(domain.Comment) | |
| 205 | + | } | |
| 206 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, int64, int64) error); ok { | |
| 207 | + | r1 = returnFunc(ctx, repo, number, id) | |
| 208 | + | } else { | |
| 209 | + | r1 = ret.Error(1) | |
| 210 | + | } | |
| 211 | + | return r0, r1 | |
| 212 | + | } | |
| 213 | + | ||
| 214 | + | // MergeRequestStore_GetComment_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetComment' | |
| 215 | + | type MergeRequestStore_GetComment_Call struct { | |
| 216 | + | *mock.Call | |
| 217 | + | } | |
| 218 | + | ||
| 219 | + | // GetComment is a helper method to define mock.On call | |
| 220 | + | // - ctx context.Context | |
| 221 | + | // - repo domain.RepoName | |
| 222 | + | // - number int64 | |
| 223 | + | // - id int64 | |
| 224 | + | func (_e *MergeRequestStore_Expecter) GetComment(ctx any, repo any, number any, id any) *MergeRequestStore_GetComment_Call { | |
| 225 | + | return &MergeRequestStore_GetComment_Call{Call: _e.mock.On("GetComment", ctx, repo, number, id)} | |
| 226 | + | } | |
| 227 | + | ||
| 228 | + | func (_c *MergeRequestStore_GetComment_Call) Run(run func(ctx context.Context, repo domain.RepoName, number int64, id int64)) *MergeRequestStore_GetComment_Call { | |
| 229 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 230 | + | var arg0 context.Context | |
| 231 | + | if args[0] != nil { | |
| 232 | + | arg0 = args[0].(context.Context) | |
| 233 | + | } | |
| 234 | + | var arg1 domain.RepoName | |
| 235 | + | if args[1] != nil { | |
| 236 | + | arg1 = args[1].(domain.RepoName) | |
| 237 | + | } | |
| 238 | + | var arg2 int64 | |
| 239 | + | if args[2] != nil { | |
| 240 | + | arg2 = args[2].(int64) | |
| 241 | + | } | |
| 242 | + | var arg3 int64 | |
| 243 | + | if args[3] != nil { | |
| 244 | + | arg3 = args[3].(int64) | |
| 245 | + | } | |
| 246 | + | run( | |
| 247 | + | arg0, | |
| 248 | + | arg1, | |
| 249 | + | arg2, | |
| 250 | + | arg3, | |
| 251 | + | ) | |
| 252 | + | }) | |
| 253 | + | return _c | |
| 254 | + | } | |
| 255 | + | ||
| 256 | + | func (_c *MergeRequestStore_GetComment_Call) Return(comment domain.Comment, err error) *MergeRequestStore_GetComment_Call { | |
| 257 | + | _c.Call.Return(comment, err) | |
| 258 | + | return _c | |
| 259 | + | } | |
| 260 | + | ||
| 261 | + | func (_c *MergeRequestStore_GetComment_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, number int64, id int64) (domain.Comment, error)) *MergeRequestStore_GetComment_Call { | |
| 262 | + | _c.Call.Return(run) | |
| 263 | + | return _c | |
| 264 | + | } | |
| 265 | + | ||
| 266 | + | // GetRead provides a mock function for the type MergeRequestStore | |
| 267 | + | func (_mock *MergeRequestStore) GetRead(ctx context.Context, repo domain.RepoName, number int64) (domain.ObjectID, error) { | |
| 268 | + | ret := _mock.Called(ctx, repo, number) | |
| 269 | + | ||
| 270 | + | if len(ret) == 0 { | |
| 271 | + | panic("no return value specified for GetRead") | |
| 272 | + | } | |
| 273 | + | ||
| 274 | + | var r0 domain.ObjectID | |
| 275 | + | var r1 error | |
| 276 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64) (domain.ObjectID, error)); ok { | |
| 277 | + | return returnFunc(ctx, repo, number) | |
| 278 | + | } | |
| 279 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64) domain.ObjectID); ok { | |
| 280 | + | r0 = returnFunc(ctx, repo, number) | |
| 281 | + | } else { | |
| 282 | + | r0 = ret.Get(0).(domain.ObjectID) | |
| 283 | + | } | |
| 284 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, int64) error); ok { | |
| 285 | + | r1 = returnFunc(ctx, repo, number) | |
| 286 | + | } else { | |
| 287 | + | r1 = ret.Error(1) | |
| 288 | + | } | |
| 289 | + | return r0, r1 | |
| 290 | + | } | |
| 291 | + | ||
| 292 | + | // MergeRequestStore_GetRead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRead' | |
| 293 | + | type MergeRequestStore_GetRead_Call struct { | |
| 294 | + | *mock.Call | |
| 295 | + | } | |
| 296 | + | ||
| 297 | + | // GetRead is a helper method to define mock.On call | |
| 298 | + | // - ctx context.Context | |
| 299 | + | // - repo domain.RepoName | |
| 300 | + | // - number int64 | |
| 301 | + | func (_e *MergeRequestStore_Expecter) GetRead(ctx any, repo any, number any) *MergeRequestStore_GetRead_Call { | |
| 302 | + | return &MergeRequestStore_GetRead_Call{Call: _e.mock.On("GetRead", ctx, repo, number)} | |
| 303 | + | } | |
| 304 | + | ||
| 305 | + | func (_c *MergeRequestStore_GetRead_Call) Run(run func(ctx context.Context, repo domain.RepoName, number int64)) *MergeRequestStore_GetRead_Call { | |
| 306 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 307 | + | var arg0 context.Context | |
| 308 | + | if args[0] != nil { | |
| 309 | + | arg0 = args[0].(context.Context) | |
| 310 | + | } | |
| 311 | + | var arg1 domain.RepoName | |
| 312 | + | if args[1] != nil { | |
| 313 | + | arg1 = args[1].(domain.RepoName) | |
| 314 | + | } | |
| 315 | + | var arg2 int64 | |
| 316 | + | if args[2] != nil { | |
| 317 | + | arg2 = args[2].(int64) | |
| 318 | + | } | |
| 319 | + | run( | |
| 320 | + | arg0, | |
| 321 | + | arg1, | |
| 322 | + | arg2, | |
| 323 | + | ) | |
| 324 | + | }) | |
| 325 | + | return _c | |
| 326 | + | } | |
| 327 | + | ||
| 328 | + | func (_c *MergeRequestStore_GetRead_Call) Return(objectID domain.ObjectID, err error) *MergeRequestStore_GetRead_Call { | |
| 329 | + | _c.Call.Return(objectID, err) | |
| 330 | + | return _c | |
| 331 | + | } | |
| 332 | + | ||
| 333 | + | func (_c *MergeRequestStore_GetRead_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, number int64) (domain.ObjectID, error)) *MergeRequestStore_GetRead_Call { | |
| 334 | + | _c.Call.Return(run) | |
| 335 | + | return _c | |
| 336 | + | } | |
| 337 | + | ||
| 338 | + | // List provides a mock function for the type MergeRequestStore | |
| 339 | + | func (_mock *MergeRequestStore) List(ctx context.Context, repo domain.RepoName) ([]domain.MergeRequest, error) { | |
| 340 | + | ret := _mock.Called(ctx, repo) | |
| 341 | + | ||
| 342 | + | if len(ret) == 0 { | |
| 343 | + | panic("no return value specified for List") | |
| 344 | + | } | |
| 345 | + | ||
| 346 | + | var r0 []domain.MergeRequest | |
| 347 | + | var r1 error | |
| 348 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) ([]domain.MergeRequest, error)); ok { | |
| 349 | + | return returnFunc(ctx, repo) | |
| 350 | + | } | |
| 351 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) []domain.MergeRequest); ok { | |
| 352 | + | r0 = returnFunc(ctx, repo) | |
| 353 | + | } else { | |
| 354 | + | if ret.Get(0) != nil { | |
| 355 | + | r0 = ret.Get(0).([]domain.MergeRequest) | |
| 356 | + | } | |
| 357 | + | } | |
| 358 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName) error); ok { | |
| 359 | + | r1 = returnFunc(ctx, repo) | |
| 360 | + | } else { | |
| 361 | + | r1 = ret.Error(1) | |
| 362 | + | } | |
| 363 | + | return r0, r1 | |
| 364 | + | } | |
| 365 | + | ||
| 366 | + | // MergeRequestStore_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List' | |
| 367 | + | type MergeRequestStore_List_Call struct { | |
| 368 | + | *mock.Call | |
| 369 | + | } | |
| 370 | + | ||
| 371 | + | // List is a helper method to define mock.On call | |
| 372 | + | // - ctx context.Context | |
| 373 | + | // - repo domain.RepoName | |
| 374 | + | func (_e *MergeRequestStore_Expecter) List(ctx any, repo any) *MergeRequestStore_List_Call { | |
| 375 | + | return &MergeRequestStore_List_Call{Call: _e.mock.On("List", ctx, repo)} | |
| 376 | + | } | |
| 377 | + | ||
| 378 | + | func (_c *MergeRequestStore_List_Call) Run(run func(ctx context.Context, repo domain.RepoName)) *MergeRequestStore_List_Call { | |
| 379 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 380 | + | var arg0 context.Context | |
| 381 | + | if args[0] != nil { | |
| 382 | + | arg0 = args[0].(context.Context) | |
| 383 | + | } | |
| 384 | + | var arg1 domain.RepoName | |
| 385 | + | if args[1] != nil { | |
| 386 | + | arg1 = args[1].(domain.RepoName) | |
| 387 | + | } | |
| 388 | + | run( | |
| 389 | + | arg0, | |
| 390 | + | arg1, | |
| 391 | + | ) | |
| 392 | + | }) | |
| 393 | + | return _c | |
| 394 | + | } | |
| 395 | + | ||
| 396 | + | func (_c *MergeRequestStore_List_Call) Return(mergeRequests []domain.MergeRequest, err error) *MergeRequestStore_List_Call { | |
| 397 | + | _c.Call.Return(mergeRequests, err) | |
| 398 | + | return _c | |
| 399 | + | } | |
| 400 | + | ||
| 401 | + | func (_c *MergeRequestStore_List_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName) ([]domain.MergeRequest, error)) *MergeRequestStore_List_Call { | |
| 402 | + | _c.Call.Return(run) | |
| 403 | + | return _c | |
| 404 | + | } | |
| 405 | + | ||
| 406 | + | // ListComments provides a mock function for the type MergeRequestStore | |
| 407 | + | func (_mock *MergeRequestStore) ListComments(ctx context.Context, repo domain.RepoName, number int64) ([]domain.Comment, error) { | |
| 408 | + | ret := _mock.Called(ctx, repo, number) | |
| 409 | + | ||
| 410 | + | if len(ret) == 0 { | |
| 411 | + | panic("no return value specified for ListComments") | |
| 412 | + | } | |
| 413 | + | ||
| 414 | + | var r0 []domain.Comment | |
| 415 | + | var r1 error | |
| 416 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64) ([]domain.Comment, error)); ok { | |
| 417 | + | return returnFunc(ctx, repo, number) | |
| 418 | + | } | |
| 419 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64) []domain.Comment); ok { | |
| 420 | + | r0 = returnFunc(ctx, repo, number) | |
| 421 | + | } else { | |
| 422 | + | if ret.Get(0) != nil { | |
| 423 | + | r0 = ret.Get(0).([]domain.Comment) | |
| 424 | + | } | |
| 425 | + | } | |
| 426 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, int64) error); ok { | |
| 427 | + | r1 = returnFunc(ctx, repo, number) | |
| 428 | + | } else { | |
| 429 | + | r1 = ret.Error(1) | |
| 430 | + | } | |
| 431 | + | return r0, r1 | |
| 432 | + | } | |
| 433 | + | ||
| 434 | + | // MergeRequestStore_ListComments_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListComments' | |
| 435 | + | type MergeRequestStore_ListComments_Call struct { | |
| 436 | + | *mock.Call | |
| 437 | + | } | |
| 438 | + | ||
| 439 | + | // ListComments is a helper method to define mock.On call | |
| 440 | + | // - ctx context.Context | |
| 441 | + | // - repo domain.RepoName | |
| 442 | + | // - number int64 | |
| 443 | + | func (_e *MergeRequestStore_Expecter) ListComments(ctx any, repo any, number any) *MergeRequestStore_ListComments_Call { | |
| 444 | + | return &MergeRequestStore_ListComments_Call{Call: _e.mock.On("ListComments", ctx, repo, number)} | |
| 445 | + | } | |
| 446 | + | ||
| 447 | + | func (_c *MergeRequestStore_ListComments_Call) Run(run func(ctx context.Context, repo domain.RepoName, number int64)) *MergeRequestStore_ListComments_Call { | |
| 448 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 449 | + | var arg0 context.Context | |
| 450 | + | if args[0] != nil { | |
| 451 | + | arg0 = args[0].(context.Context) | |
| 452 | + | } | |
| 453 | + | var arg1 domain.RepoName | |
| 454 | + | if args[1] != nil { | |
| 455 | + | arg1 = args[1].(domain.RepoName) | |
| 456 | + | } | |
| 457 | + | var arg2 int64 | |
| 458 | + | if args[2] != nil { | |
| 459 | + | arg2 = args[2].(int64) | |
| 460 | + | } | |
| 461 | + | run( | |
| 462 | + | arg0, | |
| 463 | + | arg1, | |
| 464 | + | arg2, | |
| 465 | + | ) | |
| 466 | + | }) | |
| 467 | + | return _c | |
| 468 | + | } | |
| 469 | + | ||
| 470 | + | func (_c *MergeRequestStore_ListComments_Call) Return(comments []domain.Comment, err error) *MergeRequestStore_ListComments_Call { | |
| 471 | + | _c.Call.Return(comments, err) | |
| 472 | + | return _c | |
| 473 | + | } | |
| 474 | + | ||
| 475 | + | func (_c *MergeRequestStore_ListComments_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, number int64) ([]domain.Comment, error)) *MergeRequestStore_ListComments_Call { | |
| 476 | + | _c.Call.Return(run) | |
| 477 | + | return _c | |
| 478 | + | } | |
| 479 | + | ||
| 480 | + | // SaveComment provides a mock function for the type MergeRequestStore | |
| 481 | + | func (_mock *MergeRequestStore) SaveComment(ctx context.Context, mr domain.MergeRequest, c domain.Comment) error { | |
| 482 | + | ret := _mock.Called(ctx, mr, c) | |
| 483 | + | ||
| 484 | + | if len(ret) == 0 { | |
| 485 | + | panic("no return value specified for SaveComment") | |
| 486 | + | } | |
| 487 | + | ||
| 488 | + | var r0 error | |
| 489 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.MergeRequest, domain.Comment) error); ok { | |
| 490 | + | r0 = returnFunc(ctx, mr, c) | |
| 491 | + | } else { | |
| 492 | + | r0 = ret.Error(0) | |
| 493 | + | } | |
| 494 | + | return r0 | |
| 495 | + | } | |
| 496 | + | ||
| 497 | + | // MergeRequestStore_SaveComment_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SaveComment' | |
| 498 | + | type MergeRequestStore_SaveComment_Call struct { | |
| 499 | + | *mock.Call | |
| 500 | + | } | |
| 501 | + | ||
| 502 | + | // SaveComment is a helper method to define mock.On call | |
| 503 | + | // - ctx context.Context | |
| 504 | + | // - mr domain.MergeRequest | |
| 505 | + | // - c domain.Comment | |
| 506 | + | func (_e *MergeRequestStore_Expecter) SaveComment(ctx any, mr any, c any) *MergeRequestStore_SaveComment_Call { | |
| 507 | + | return &MergeRequestStore_SaveComment_Call{Call: _e.mock.On("SaveComment", ctx, mr, c)} | |
| 508 | + | } | |
| 509 | + | ||
| 510 | + | func (_c *MergeRequestStore_SaveComment_Call) Run(run func(ctx context.Context, mr domain.MergeRequest, c domain.Comment)) *MergeRequestStore_SaveComment_Call { | |
| 511 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 512 | + | var arg0 context.Context | |
| 513 | + | if args[0] != nil { | |
| 514 | + | arg0 = args[0].(context.Context) | |
| 515 | + | } | |
| 516 | + | var arg1 domain.MergeRequest | |
| 517 | + | if args[1] != nil { | |
| 518 | + | arg1 = args[1].(domain.MergeRequest) | |
| 519 | + | } | |
| 520 | + | var arg2 domain.Comment | |
| 521 | + | if args[2] != nil { | |
| 522 | + | arg2 = args[2].(domain.Comment) | |
| 523 | + | } | |
| 524 | + | run( | |
| 525 | + | arg0, | |
| 526 | + | arg1, | |
| 527 | + | arg2, | |
| 528 | + | ) | |
| 529 | + | }) | |
| 530 | + | return _c | |
| 531 | + | } | |
| 532 | + | ||
| 533 | + | func (_c *MergeRequestStore_SaveComment_Call) Return(err error) *MergeRequestStore_SaveComment_Call { | |
| 534 | + | _c.Call.Return(err) | |
| 535 | + | return _c | |
| 536 | + | } | |
| 537 | + | ||
| 538 | + | func (_c *MergeRequestStore_SaveComment_Call) RunAndReturn(run func(ctx context.Context, mr domain.MergeRequest, c domain.Comment) error) *MergeRequestStore_SaveComment_Call { | |
| 539 | + | _c.Call.Return(run) | |
| 540 | + | return _c | |
| 541 | + | } | |
| 542 | + | ||
| 543 | + | // SetRead provides a mock function for the type MergeRequestStore | |
| 544 | + | func (_mock *MergeRequestStore) SetRead(ctx context.Context, repo domain.RepoName, number int64, head domain.ObjectID) error { | |
| 545 | + | ret := _mock.Called(ctx, repo, number, head) | |
| 546 | + | ||
| 547 | + | if len(ret) == 0 { | |
| 548 | + | panic("no return value specified for SetRead") | |
| 549 | + | } | |
| 550 | + | ||
| 551 | + | var r0 error | |
| 552 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64, domain.ObjectID) error); ok { | |
| 553 | + | r0 = returnFunc(ctx, repo, number, head) | |
| 554 | + | } else { | |
| 555 | + | r0 = ret.Error(0) | |
| 556 | + | } | |
| 557 | + | return r0 | |
| 558 | + | } | |
| 559 | + | ||
| 560 | + | // MergeRequestStore_SetRead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetRead' | |
| 561 | + | type MergeRequestStore_SetRead_Call struct { | |
| 562 | + | *mock.Call | |
| 563 | + | } | |
| 564 | + | ||
| 565 | + | // SetRead is a helper method to define mock.On call | |
| 566 | + | // - ctx context.Context | |
| 567 | + | // - repo domain.RepoName | |
| 568 | + | // - number int64 | |
| 569 | + | // - head domain.ObjectID | |
| 570 | + | func (_e *MergeRequestStore_Expecter) SetRead(ctx any, repo any, number any, head any) *MergeRequestStore_SetRead_Call { | |
| 571 | + | return &MergeRequestStore_SetRead_Call{Call: _e.mock.On("SetRead", ctx, repo, number, head)} | |
| 572 | + | } | |
| 573 | + | ||
| 574 | + | func (_c *MergeRequestStore_SetRead_Call) Run(run func(ctx context.Context, repo domain.RepoName, number int64, head domain.ObjectID)) *MergeRequestStore_SetRead_Call { | |
| 575 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 576 | + | var arg0 context.Context | |
| 577 | + | if args[0] != nil { | |
| 578 | + | arg0 = args[0].(context.Context) | |
| 579 | + | } | |
| 580 | + | var arg1 domain.RepoName | |
| 581 | + | if args[1] != nil { | |
| 582 | + | arg1 = args[1].(domain.RepoName) | |
| 583 | + | } | |
| 584 | + | var arg2 int64 | |
| 585 | + | if args[2] != nil { | |
| 586 | + | arg2 = args[2].(int64) | |
| 587 | + | } | |
| 588 | + | var arg3 domain.ObjectID | |
| 589 | + | if args[3] != nil { | |
| 590 | + | arg3 = args[3].(domain.ObjectID) | |
| 591 | + | } | |
| 592 | + | run( | |
| 593 | + | arg0, | |
| 594 | + | arg1, | |
| 595 | + | arg2, | |
| 596 | + | arg3, | |
| 597 | + | ) | |
| 598 | + | }) | |
| 599 | + | return _c | |
| 600 | + | } | |
| 601 | + | ||
| 602 | + | func (_c *MergeRequestStore_SetRead_Call) Return(err error) *MergeRequestStore_SetRead_Call { | |
| 603 | + | _c.Call.Return(err) | |
| 604 | + | return _c | |
| 605 | + | } | |
| 606 | + | ||
| 607 | + | func (_c *MergeRequestStore_SetRead_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, number int64, head domain.ObjectID) error) *MergeRequestStore_SetRead_Call { | |
| 608 | + | _c.Call.Return(run) | |
| 609 | + | return _c | |
| 610 | + | } | |
| 611 | + | ||
| 612 | + | // Update provides a mock function for the type MergeRequestStore | |
| 613 | + | func (_mock *MergeRequestStore) Update(ctx context.Context, mr domain.MergeRequest) error { | |
| 614 | + | ret := _mock.Called(ctx, mr) | |
| 615 | + | ||
| 616 | + | if len(ret) == 0 { | |
| 617 | + | panic("no return value specified for Update") | |
| 618 | + | } | |
| 619 | + | ||
| 620 | + | var r0 error | |
| 621 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.MergeRequest) error); ok { | |
| 622 | + | r0 = returnFunc(ctx, mr) | |
| 623 | + | } else { | |
| 624 | + | r0 = ret.Error(0) | |
| 625 | + | } | |
| 626 | + | return r0 | |
| 627 | + | } | |
| 628 | + | ||
| 629 | + | // MergeRequestStore_Update_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Update' | |
| 630 | + | type MergeRequestStore_Update_Call struct { | |
| 631 | + | *mock.Call | |
| 632 | + | } | |
| 633 | + | ||
| 634 | + | // Update is a helper method to define mock.On call | |
| 635 | + | // - ctx context.Context | |
| 636 | + | // - mr domain.MergeRequest | |
| 637 | + | func (_e *MergeRequestStore_Expecter) Update(ctx any, mr any) *MergeRequestStore_Update_Call { | |
| 638 | + | return &MergeRequestStore_Update_Call{Call: _e.mock.On("Update", ctx, mr)} | |
| 639 | + | } | |
| 640 | + | ||
| 641 | + | func (_c *MergeRequestStore_Update_Call) Run(run func(ctx context.Context, mr domain.MergeRequest)) *MergeRequestStore_Update_Call { | |
| 642 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 643 | + | var arg0 context.Context | |
| 644 | + | if args[0] != nil { | |
| 645 | + | arg0 = args[0].(context.Context) | |
| 646 | + | } | |
| 647 | + | var arg1 domain.MergeRequest | |
| 648 | + | if args[1] != nil { | |
| 649 | + | arg1 = args[1].(domain.MergeRequest) | |
| 650 | + | } | |
| 651 | + | run( | |
| 652 | + | arg0, | |
| 653 | + | arg1, | |
| 654 | + | ) | |
| 655 | + | }) | |
| 656 | + | return _c | |
| 657 | + | } | |
| 658 | + | ||
| 659 | + | func (_c *MergeRequestStore_Update_Call) Return(err error) *MergeRequestStore_Update_Call { | |
| 660 | + | _c.Call.Return(err) | |
| 661 | + | return _c | |
| 662 | + | } | |
| 663 | + | ||
| 664 | + | func (_c *MergeRequestStore_Update_Call) RunAndReturn(run func(ctx context.Context, mr domain.MergeRequest) error) *MergeRequestStore_Update_Call { | |
| 665 | + | _c.Call.Return(run) | |
| 666 | + | return _c | |
| 667 | + | } | |
| @@ -0,0 +1,667 @@ | |||||
| 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 | + | // NewMergeRequestStore creates a new instance of MergeRequestStore. 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 NewMergeRequestStore(t interface { | |||
| 17 | + | mock.TestingT | |||
| 18 | + | Cleanup(func()) | |||
| 19 | + | }) *MergeRequestStore { | |||
| 20 | + | if helper, ok := t.(interface{ Helper() }); ok { | |||
| 21 | + | helper.Helper() | |||
| 22 | + | } | |||
| 23 | + | ||||
| 24 | + | mock := &MergeRequestStore{} | |||
| 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 | + | // MergeRequestStore is an autogenerated mock type for the MergeRequestStore type | |||
| 38 | + | type MergeRequestStore struct { | |||
| 39 | + | mock.Mock | |||
| 40 | + | } | |||
| 41 | + | ||||
| 42 | + | type MergeRequestStore_Expecter struct { | |||
| 43 | + | mock *mock.Mock | |||
| 44 | + | } | |||
| 45 | + | ||||
| 46 | + | func (_m *MergeRequestStore) EXPECT() *MergeRequestStore_Expecter { | |||
| 47 | + | return &MergeRequestStore_Expecter{mock: &_m.Mock} | |||
| 48 | + | } | |||
| 49 | + | ||||
| 50 | + | // Create provides a mock function for the type MergeRequestStore | |||
| 51 | + | func (_mock *MergeRequestStore) Create(ctx context.Context, mr domain.MergeRequest) (domain.MergeRequest, error) { | |||
| 52 | + | ret := _mock.Called(ctx, mr) | |||
| 53 | + | ||||
| 54 | + | if len(ret) == 0 { | |||
| 55 | + | panic("no return value specified for Create") | |||
| 56 | + | } | |||
| 57 | + | ||||
| 58 | + | var r0 domain.MergeRequest | |||
| 59 | + | var r1 error | |||
| 60 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.MergeRequest) (domain.MergeRequest, error)); ok { | |||
| 61 | + | return returnFunc(ctx, mr) | |||
| 62 | + | } | |||
| 63 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.MergeRequest) domain.MergeRequest); ok { | |||
| 64 | + | r0 = returnFunc(ctx, mr) | |||
| 65 | + | } else { | |||
| 66 | + | r0 = ret.Get(0).(domain.MergeRequest) | |||
| 67 | + | } | |||
| 68 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.MergeRequest) error); ok { | |||
| 69 | + | r1 = returnFunc(ctx, mr) | |||
| 70 | + | } else { | |||
| 71 | + | r1 = ret.Error(1) | |||
| 72 | + | } | |||
| 73 | + | return r0, r1 | |||
| 74 | + | } | |||
| 75 | + | ||||
| 76 | + | // MergeRequestStore_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create' | |||
| 77 | + | type MergeRequestStore_Create_Call struct { | |||
| 78 | + | *mock.Call | |||
| 79 | + | } | |||
| 80 | + | ||||
| 81 | + | // Create is a helper method to define mock.On call | |||
| 82 | + | // - ctx context.Context | |||
| 83 | + | // - mr domain.MergeRequest | |||
| 84 | + | func (_e *MergeRequestStore_Expecter) Create(ctx any, mr any) *MergeRequestStore_Create_Call { | |||
| 85 | + | return &MergeRequestStore_Create_Call{Call: _e.mock.On("Create", ctx, mr)} | |||
| 86 | + | } | |||
| 87 | + | ||||
| 88 | + | func (_c *MergeRequestStore_Create_Call) Run(run func(ctx context.Context, mr domain.MergeRequest)) *MergeRequestStore_Create_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 domain.MergeRequest | |||
| 95 | + | if args[1] != nil { | |||
| 96 | + | arg1 = args[1].(domain.MergeRequest) | |||
| 97 | + | } | |||
| 98 | + | run( | |||
| 99 | + | arg0, | |||
| 100 | + | arg1, | |||
| 101 | + | ) | |||
| 102 | + | }) | |||
| 103 | + | return _c | |||
| 104 | + | } | |||
| 105 | + | ||||
| 106 | + | func (_c *MergeRequestStore_Create_Call) Return(mergeRequest domain.MergeRequest, err error) *MergeRequestStore_Create_Call { | |||
| 107 | + | _c.Call.Return(mergeRequest, err) | |||
| 108 | + | return _c | |||
| 109 | + | } | |||
| 110 | + | ||||
| 111 | + | func (_c *MergeRequestStore_Create_Call) RunAndReturn(run func(ctx context.Context, mr domain.MergeRequest) (domain.MergeRequest, error)) *MergeRequestStore_Create_Call { | |||
| 112 | + | _c.Call.Return(run) | |||
| 113 | + | return _c | |||
| 114 | + | } | |||
| 115 | + | ||||
| 116 | + | // Get provides a mock function for the type MergeRequestStore | |||
| 117 | + | func (_mock *MergeRequestStore) Get(ctx context.Context, repo domain.RepoName, number int64) (domain.MergeRequest, error) { | |||
| 118 | + | ret := _mock.Called(ctx, repo, number) | |||
| 119 | + | ||||
| 120 | + | if len(ret) == 0 { | |||
| 121 | + | panic("no return value specified for Get") | |||
| 122 | + | } | |||
| 123 | + | ||||
| 124 | + | var r0 domain.MergeRequest | |||
| 125 | + | var r1 error | |||
| 126 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64) (domain.MergeRequest, error)); ok { | |||
| 127 | + | return returnFunc(ctx, repo, number) | |||
| 128 | + | } | |||
| 129 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64) domain.MergeRequest); ok { | |||
| 130 | + | r0 = returnFunc(ctx, repo, number) | |||
| 131 | + | } else { | |||
| 132 | + | r0 = ret.Get(0).(domain.MergeRequest) | |||
| 133 | + | } | |||
| 134 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, int64) error); ok { | |||
| 135 | + | r1 = returnFunc(ctx, repo, number) | |||
| 136 | + | } else { | |||
| 137 | + | r1 = ret.Error(1) | |||
| 138 | + | } | |||
| 139 | + | return r0, r1 | |||
| 140 | + | } | |||
| 141 | + | ||||
| 142 | + | // MergeRequestStore_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' | |||
| 143 | + | type MergeRequestStore_Get_Call struct { | |||
| 144 | + | *mock.Call | |||
| 145 | + | } | |||
| 146 | + | ||||
| 147 | + | // Get is a helper method to define mock.On call | |||
| 148 | + | // - ctx context.Context | |||
| 149 | + | // - repo domain.RepoName | |||
| 150 | + | // - number int64 | |||
| 151 | + | func (_e *MergeRequestStore_Expecter) Get(ctx any, repo any, number any) *MergeRequestStore_Get_Call { | |||
| 152 | + | return &MergeRequestStore_Get_Call{Call: _e.mock.On("Get", ctx, repo, number)} | |||
| 153 | + | } | |||
| 154 | + | ||||
| 155 | + | func (_c *MergeRequestStore_Get_Call) Run(run func(ctx context.Context, repo domain.RepoName, number int64)) *MergeRequestStore_Get_Call { | |||
| 156 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 157 | + | var arg0 context.Context | |||
| 158 | + | if args[0] != nil { | |||
| 159 | + | arg0 = args[0].(context.Context) | |||
| 160 | + | } | |||
| 161 | + | var arg1 domain.RepoName | |||
| 162 | + | if args[1] != nil { | |||
| 163 | + | arg1 = args[1].(domain.RepoName) | |||
| 164 | + | } | |||
| 165 | + | var arg2 int64 | |||
| 166 | + | if args[2] != nil { | |||
| 167 | + | arg2 = args[2].(int64) | |||
| 168 | + | } | |||
| 169 | + | run( | |||
| 170 | + | arg0, | |||
| 171 | + | arg1, | |||
| 172 | + | arg2, | |||
| 173 | + | ) | |||
| 174 | + | }) | |||
| 175 | + | return _c | |||
| 176 | + | } | |||
| 177 | + | ||||
| 178 | + | func (_c *MergeRequestStore_Get_Call) Return(mergeRequest domain.MergeRequest, err error) *MergeRequestStore_Get_Call { | |||
| 179 | + | _c.Call.Return(mergeRequest, err) | |||
| 180 | + | return _c | |||
| 181 | + | } | |||
| 182 | + | ||||
| 183 | + | func (_c *MergeRequestStore_Get_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, number int64) (domain.MergeRequest, error)) *MergeRequestStore_Get_Call { | |||
| 184 | + | _c.Call.Return(run) | |||
| 185 | + | return _c | |||
| 186 | + | } | |||
| 187 | + | ||||
| 188 | + | // GetComment provides a mock function for the type MergeRequestStore | |||
| 189 | + | func (_mock *MergeRequestStore) GetComment(ctx context.Context, repo domain.RepoName, number int64, id int64) (domain.Comment, error) { | |||
| 190 | + | ret := _mock.Called(ctx, repo, number, id) | |||
| 191 | + | ||||
| 192 | + | if len(ret) == 0 { | |||
| 193 | + | panic("no return value specified for GetComment") | |||
| 194 | + | } | |||
| 195 | + | ||||
| 196 | + | var r0 domain.Comment | |||
| 197 | + | var r1 error | |||
| 198 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64, int64) (domain.Comment, error)); ok { | |||
| 199 | + | return returnFunc(ctx, repo, number, id) | |||
| 200 | + | } | |||
| 201 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64, int64) domain.Comment); ok { | |||
| 202 | + | r0 = returnFunc(ctx, repo, number, id) | |||
| 203 | + | } else { | |||
| 204 | + | r0 = ret.Get(0).(domain.Comment) | |||
| 205 | + | } | |||
| 206 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, int64, int64) error); ok { | |||
| 207 | + | r1 = returnFunc(ctx, repo, number, id) | |||
| 208 | + | } else { | |||
| 209 | + | r1 = ret.Error(1) | |||
| 210 | + | } | |||
| 211 | + | return r0, r1 | |||
| 212 | + | } | |||
| 213 | + | ||||
| 214 | + | // MergeRequestStore_GetComment_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetComment' | |||
| 215 | + | type MergeRequestStore_GetComment_Call struct { | |||
| 216 | + | *mock.Call | |||
| 217 | + | } | |||
| 218 | + | ||||
| 219 | + | // GetComment is a helper method to define mock.On call | |||
| 220 | + | // - ctx context.Context | |||
| 221 | + | // - repo domain.RepoName | |||
| 222 | + | // - number int64 | |||
| 223 | + | // - id int64 | |||
| 224 | + | func (_e *MergeRequestStore_Expecter) GetComment(ctx any, repo any, number any, id any) *MergeRequestStore_GetComment_Call { | |||
| 225 | + | return &MergeRequestStore_GetComment_Call{Call: _e.mock.On("GetComment", ctx, repo, number, id)} | |||
| 226 | + | } | |||
| 227 | + | ||||
| 228 | + | func (_c *MergeRequestStore_GetComment_Call) Run(run func(ctx context.Context, repo domain.RepoName, number int64, id int64)) *MergeRequestStore_GetComment_Call { | |||
| 229 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 230 | + | var arg0 context.Context | |||
| 231 | + | if args[0] != nil { | |||
| 232 | + | arg0 = args[0].(context.Context) | |||
| 233 | + | } | |||
| 234 | + | var arg1 domain.RepoName | |||
| 235 | + | if args[1] != nil { | |||
| 236 | + | arg1 = args[1].(domain.RepoName) | |||
| 237 | + | } | |||
| 238 | + | var arg2 int64 | |||
| 239 | + | if args[2] != nil { | |||
| 240 | + | arg2 = args[2].(int64) | |||
| 241 | + | } | |||
| 242 | + | var arg3 int64 | |||
| 243 | + | if args[3] != nil { | |||
| 244 | + | arg3 = args[3].(int64) | |||
| 245 | + | } | |||
| 246 | + | run( | |||
| 247 | + | arg0, | |||
| 248 | + | arg1, | |||
| 249 | + | arg2, | |||
| 250 | + | arg3, | |||
| 251 | + | ) | |||
| 252 | + | }) | |||
| 253 | + | return _c | |||
| 254 | + | } | |||
| 255 | + | ||||
| 256 | + | func (_c *MergeRequestStore_GetComment_Call) Return(comment domain.Comment, err error) *MergeRequestStore_GetComment_Call { | |||
| 257 | + | _c.Call.Return(comment, err) | |||
| 258 | + | return _c | |||
| 259 | + | } | |||
| 260 | + | ||||
| 261 | + | func (_c *MergeRequestStore_GetComment_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, number int64, id int64) (domain.Comment, error)) *MergeRequestStore_GetComment_Call { | |||
| 262 | + | _c.Call.Return(run) | |||
| 263 | + | return _c | |||
| 264 | + | } | |||
| 265 | + | ||||
| 266 | + | // GetRead provides a mock function for the type MergeRequestStore | |||
| 267 | + | func (_mock *MergeRequestStore) GetRead(ctx context.Context, repo domain.RepoName, number int64) (domain.ObjectID, error) { | |||
| 268 | + | ret := _mock.Called(ctx, repo, number) | |||
| 269 | + | ||||
| 270 | + | if len(ret) == 0 { | |||
| 271 | + | panic("no return value specified for GetRead") | |||
| 272 | + | } | |||
| 273 | + | ||||
| 274 | + | var r0 domain.ObjectID | |||
| 275 | + | var r1 error | |||
| 276 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64) (domain.ObjectID, error)); ok { | |||
| 277 | + | return returnFunc(ctx, repo, number) | |||
| 278 | + | } | |||
| 279 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64) domain.ObjectID); ok { | |||
| 280 | + | r0 = returnFunc(ctx, repo, number) | |||
| 281 | + | } else { | |||
| 282 | + | r0 = ret.Get(0).(domain.ObjectID) | |||
| 283 | + | } | |||
| 284 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, int64) error); ok { | |||
| 285 | + | r1 = returnFunc(ctx, repo, number) | |||
| 286 | + | } else { | |||
| 287 | + | r1 = ret.Error(1) | |||
| 288 | + | } | |||
| 289 | + | return r0, r1 | |||
| 290 | + | } | |||
| 291 | + | ||||
| 292 | + | // MergeRequestStore_GetRead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRead' | |||
| 293 | + | type MergeRequestStore_GetRead_Call struct { | |||
| 294 | + | *mock.Call | |||
| 295 | + | } | |||
| 296 | + | ||||
| 297 | + | // GetRead is a helper method to define mock.On call | |||
| 298 | + | // - ctx context.Context | |||
| 299 | + | // - repo domain.RepoName | |||
| 300 | + | // - number int64 | |||
| 301 | + | func (_e *MergeRequestStore_Expecter) GetRead(ctx any, repo any, number any) *MergeRequestStore_GetRead_Call { | |||
| 302 | + | return &MergeRequestStore_GetRead_Call{Call: _e.mock.On("GetRead", ctx, repo, number)} | |||
| 303 | + | } | |||
| 304 | + | ||||
| 305 | + | func (_c *MergeRequestStore_GetRead_Call) Run(run func(ctx context.Context, repo domain.RepoName, number int64)) *MergeRequestStore_GetRead_Call { | |||
| 306 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 307 | + | var arg0 context.Context | |||
| 308 | + | if args[0] != nil { | |||
| 309 | + | arg0 = args[0].(context.Context) | |||
| 310 | + | } | |||
| 311 | + | var arg1 domain.RepoName | |||
| 312 | + | if args[1] != nil { | |||
| 313 | + | arg1 = args[1].(domain.RepoName) | |||
| 314 | + | } | |||
| 315 | + | var arg2 int64 | |||
| 316 | + | if args[2] != nil { | |||
| 317 | + | arg2 = args[2].(int64) | |||
| 318 | + | } | |||
| 319 | + | run( | |||
| 320 | + | arg0, | |||
| 321 | + | arg1, | |||
| 322 | + | arg2, | |||
| 323 | + | ) | |||
| 324 | + | }) | |||
| 325 | + | return _c | |||
| 326 | + | } | |||
| 327 | + | ||||
| 328 | + | func (_c *MergeRequestStore_GetRead_Call) Return(objectID domain.ObjectID, err error) *MergeRequestStore_GetRead_Call { | |||
| 329 | + | _c.Call.Return(objectID, err) | |||
| 330 | + | return _c | |||
| 331 | + | } | |||
| 332 | + | ||||
| 333 | + | func (_c *MergeRequestStore_GetRead_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, number int64) (domain.ObjectID, error)) *MergeRequestStore_GetRead_Call { | |||
| 334 | + | _c.Call.Return(run) | |||
| 335 | + | return _c | |||
| 336 | + | } | |||
| 337 | + | ||||
| 338 | + | // List provides a mock function for the type MergeRequestStore | |||
| 339 | + | func (_mock *MergeRequestStore) List(ctx context.Context, repo domain.RepoName) ([]domain.MergeRequest, error) { | |||
| 340 | + | ret := _mock.Called(ctx, repo) | |||
| 341 | + | ||||
| 342 | + | if len(ret) == 0 { | |||
| 343 | + | panic("no return value specified for List") | |||
| 344 | + | } | |||
| 345 | + | ||||
| 346 | + | var r0 []domain.MergeRequest | |||
| 347 | + | var r1 error | |||
| 348 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) ([]domain.MergeRequest, error)); ok { | |||
| 349 | + | return returnFunc(ctx, repo) | |||
| 350 | + | } | |||
| 351 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) []domain.MergeRequest); ok { | |||
| 352 | + | r0 = returnFunc(ctx, repo) | |||
| 353 | + | } else { | |||
| 354 | + | if ret.Get(0) != nil { | |||
| 355 | + | r0 = ret.Get(0).([]domain.MergeRequest) | |||
| 356 | + | } | |||
| 357 | + | } | |||
| 358 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName) error); ok { | |||
| 359 | + | r1 = returnFunc(ctx, repo) | |||
| 360 | + | } else { | |||
| 361 | + | r1 = ret.Error(1) | |||
| 362 | + | } | |||
| 363 | + | return r0, r1 | |||
| 364 | + | } | |||
| 365 | + | ||||
| 366 | + | // MergeRequestStore_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List' | |||
| 367 | + | type MergeRequestStore_List_Call struct { | |||
| 368 | + | *mock.Call | |||
| 369 | + | } | |||
| 370 | + | ||||
| 371 | + | // List is a helper method to define mock.On call | |||
| 372 | + | // - ctx context.Context | |||
| 373 | + | // - repo domain.RepoName | |||
| 374 | + | func (_e *MergeRequestStore_Expecter) List(ctx any, repo any) *MergeRequestStore_List_Call { | |||
| 375 | + | return &MergeRequestStore_List_Call{Call: _e.mock.On("List", ctx, repo)} | |||
| 376 | + | } | |||
| 377 | + | ||||
| 378 | + | func (_c *MergeRequestStore_List_Call) Run(run func(ctx context.Context, repo domain.RepoName)) *MergeRequestStore_List_Call { | |||
| 379 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 380 | + | var arg0 context.Context | |||
| 381 | + | if args[0] != nil { | |||
| 382 | + | arg0 = args[0].(context.Context) | |||
| 383 | + | } | |||
| 384 | + | var arg1 domain.RepoName | |||
| 385 | + | if args[1] != nil { | |||
| 386 | + | arg1 = args[1].(domain.RepoName) | |||
| 387 | + | } | |||
| 388 | + | run( | |||
| 389 | + | arg0, | |||
| 390 | + | arg1, | |||
| 391 | + | ) | |||
| 392 | + | }) | |||
| 393 | + | return _c | |||
| 394 | + | } | |||
| 395 | + | ||||
| 396 | + | func (_c *MergeRequestStore_List_Call) Return(mergeRequests []domain.MergeRequest, err error) *MergeRequestStore_List_Call { | |||
| 397 | + | _c.Call.Return(mergeRequests, err) | |||
| 398 | + | return _c | |||
| 399 | + | } | |||
| 400 | + | ||||
| 401 | + | func (_c *MergeRequestStore_List_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName) ([]domain.MergeRequest, error)) *MergeRequestStore_List_Call { | |||
| 402 | + | _c.Call.Return(run) | |||
| 403 | + | return _c | |||
| 404 | + | } | |||
| 405 | + | ||||
| 406 | + | // ListComments provides a mock function for the type MergeRequestStore | |||
| 407 | + | func (_mock *MergeRequestStore) ListComments(ctx context.Context, repo domain.RepoName, number int64) ([]domain.Comment, error) { | |||
| 408 | + | ret := _mock.Called(ctx, repo, number) | |||
| 409 | + | ||||
| 410 | + | if len(ret) == 0 { | |||
| 411 | + | panic("no return value specified for ListComments") | |||
| 412 | + | } | |||
| 413 | + | ||||
| 414 | + | var r0 []domain.Comment | |||
| 415 | + | var r1 error | |||
| 416 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64) ([]domain.Comment, error)); ok { | |||
| 417 | + | return returnFunc(ctx, repo, number) | |||
| 418 | + | } | |||
| 419 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64) []domain.Comment); ok { | |||
| 420 | + | r0 = returnFunc(ctx, repo, number) | |||
| 421 | + | } else { | |||
| 422 | + | if ret.Get(0) != nil { | |||
| 423 | + | r0 = ret.Get(0).([]domain.Comment) | |||
| 424 | + | } | |||
| 425 | + | } | |||
| 426 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName, int64) error); ok { | |||
| 427 | + | r1 = returnFunc(ctx, repo, number) | |||
| 428 | + | } else { | |||
| 429 | + | r1 = ret.Error(1) | |||
| 430 | + | } | |||
| 431 | + | return r0, r1 | |||
| 432 | + | } | |||
| 433 | + | ||||
| 434 | + | // MergeRequestStore_ListComments_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListComments' | |||
| 435 | + | type MergeRequestStore_ListComments_Call struct { | |||
| 436 | + | *mock.Call | |||
| 437 | + | } | |||
| 438 | + | ||||
| 439 | + | // ListComments is a helper method to define mock.On call | |||
| 440 | + | // - ctx context.Context | |||
| 441 | + | // - repo domain.RepoName | |||
| 442 | + | // - number int64 | |||
| 443 | + | func (_e *MergeRequestStore_Expecter) ListComments(ctx any, repo any, number any) *MergeRequestStore_ListComments_Call { | |||
| 444 | + | return &MergeRequestStore_ListComments_Call{Call: _e.mock.On("ListComments", ctx, repo, number)} | |||
| 445 | + | } | |||
| 446 | + | ||||
| 447 | + | func (_c *MergeRequestStore_ListComments_Call) Run(run func(ctx context.Context, repo domain.RepoName, number int64)) *MergeRequestStore_ListComments_Call { | |||
| 448 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 449 | + | var arg0 context.Context | |||
| 450 | + | if args[0] != nil { | |||
| 451 | + | arg0 = args[0].(context.Context) | |||
| 452 | + | } | |||
| 453 | + | var arg1 domain.RepoName | |||
| 454 | + | if args[1] != nil { | |||
| 455 | + | arg1 = args[1].(domain.RepoName) | |||
| 456 | + | } | |||
| 457 | + | var arg2 int64 | |||
| 458 | + | if args[2] != nil { | |||
| 459 | + | arg2 = args[2].(int64) | |||
| 460 | + | } | |||
| 461 | + | run( | |||
| 462 | + | arg0, | |||
| 463 | + | arg1, | |||
| 464 | + | arg2, | |||
| 465 | + | ) | |||
| 466 | + | }) | |||
| 467 | + | return _c | |||
| 468 | + | } | |||
| 469 | + | ||||
| 470 | + | func (_c *MergeRequestStore_ListComments_Call) Return(comments []domain.Comment, err error) *MergeRequestStore_ListComments_Call { | |||
| 471 | + | _c.Call.Return(comments, err) | |||
| 472 | + | return _c | |||
| 473 | + | } | |||
| 474 | + | ||||
| 475 | + | func (_c *MergeRequestStore_ListComments_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, number int64) ([]domain.Comment, error)) *MergeRequestStore_ListComments_Call { | |||
| 476 | + | _c.Call.Return(run) | |||
| 477 | + | return _c | |||
| 478 | + | } | |||
| 479 | + | ||||
| 480 | + | // SaveComment provides a mock function for the type MergeRequestStore | |||
| 481 | + | func (_mock *MergeRequestStore) SaveComment(ctx context.Context, mr domain.MergeRequest, c domain.Comment) error { | |||
| 482 | + | ret := _mock.Called(ctx, mr, c) | |||
| 483 | + | ||||
| 484 | + | if len(ret) == 0 { | |||
| 485 | + | panic("no return value specified for SaveComment") | |||
| 486 | + | } | |||
| 487 | + | ||||
| 488 | + | var r0 error | |||
| 489 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.MergeRequest, domain.Comment) error); ok { | |||
| 490 | + | r0 = returnFunc(ctx, mr, c) | |||
| 491 | + | } else { | |||
| 492 | + | r0 = ret.Error(0) | |||
| 493 | + | } | |||
| 494 | + | return r0 | |||
| 495 | + | } | |||
| 496 | + | ||||
| 497 | + | // MergeRequestStore_SaveComment_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SaveComment' | |||
| 498 | + | type MergeRequestStore_SaveComment_Call struct { | |||
| 499 | + | *mock.Call | |||
| 500 | + | } | |||
| 501 | + | ||||
| 502 | + | // SaveComment is a helper method to define mock.On call | |||
| 503 | + | // - ctx context.Context | |||
| 504 | + | // - mr domain.MergeRequest | |||
| 505 | + | // - c domain.Comment | |||
| 506 | + | func (_e *MergeRequestStore_Expecter) SaveComment(ctx any, mr any, c any) *MergeRequestStore_SaveComment_Call { | |||
| 507 | + | return &MergeRequestStore_SaveComment_Call{Call: _e.mock.On("SaveComment", ctx, mr, c)} | |||
| 508 | + | } | |||
| 509 | + | ||||
| 510 | + | func (_c *MergeRequestStore_SaveComment_Call) Run(run func(ctx context.Context, mr domain.MergeRequest, c domain.Comment)) *MergeRequestStore_SaveComment_Call { | |||
| 511 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 512 | + | var arg0 context.Context | |||
| 513 | + | if args[0] != nil { | |||
| 514 | + | arg0 = args[0].(context.Context) | |||
| 515 | + | } | |||
| 516 | + | var arg1 domain.MergeRequest | |||
| 517 | + | if args[1] != nil { | |||
| 518 | + | arg1 = args[1].(domain.MergeRequest) | |||
| 519 | + | } | |||
| 520 | + | var arg2 domain.Comment | |||
| 521 | + | if args[2] != nil { | |||
| 522 | + | arg2 = args[2].(domain.Comment) | |||
| 523 | + | } | |||
| 524 | + | run( | |||
| 525 | + | arg0, | |||
| 526 | + | arg1, | |||
| 527 | + | arg2, | |||
| 528 | + | ) | |||
| 529 | + | }) | |||
| 530 | + | return _c | |||
| 531 | + | } | |||
| 532 | + | ||||
| 533 | + | func (_c *MergeRequestStore_SaveComment_Call) Return(err error) *MergeRequestStore_SaveComment_Call { | |||
| 534 | + | _c.Call.Return(err) | |||
| 535 | + | return _c | |||
| 536 | + | } | |||
| 537 | + | ||||
| 538 | + | func (_c *MergeRequestStore_SaveComment_Call) RunAndReturn(run func(ctx context.Context, mr domain.MergeRequest, c domain.Comment) error) *MergeRequestStore_SaveComment_Call { | |||
| 539 | + | _c.Call.Return(run) | |||
| 540 | + | return _c | |||
| 541 | + | } | |||
| 542 | + | ||||
| 543 | + | // SetRead provides a mock function for the type MergeRequestStore | |||
| 544 | + | func (_mock *MergeRequestStore) SetRead(ctx context.Context, repo domain.RepoName, number int64, head domain.ObjectID) error { | |||
| 545 | + | ret := _mock.Called(ctx, repo, number, head) | |||
| 546 | + | ||||
| 547 | + | if len(ret) == 0 { | |||
| 548 | + | panic("no return value specified for SetRead") | |||
| 549 | + | } | |||
| 550 | + | ||||
| 551 | + | var r0 error | |||
| 552 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName, int64, domain.ObjectID) error); ok { | |||
| 553 | + | r0 = returnFunc(ctx, repo, number, head) | |||
| 554 | + | } else { | |||
| 555 | + | r0 = ret.Error(0) | |||
| 556 | + | } | |||
| 557 | + | return r0 | |||
| 558 | + | } | |||
| 559 | + | ||||
| 560 | + | // MergeRequestStore_SetRead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetRead' | |||
| 561 | + | type MergeRequestStore_SetRead_Call struct { | |||
| 562 | + | *mock.Call | |||
| 563 | + | } | |||
| 564 | + | ||||
| 565 | + | // SetRead is a helper method to define mock.On call | |||
| 566 | + | // - ctx context.Context | |||
| 567 | + | // - repo domain.RepoName | |||
| 568 | + | // - number int64 | |||
| 569 | + | // - head domain.ObjectID | |||
| 570 | + | func (_e *MergeRequestStore_Expecter) SetRead(ctx any, repo any, number any, head any) *MergeRequestStore_SetRead_Call { | |||
| 571 | + | return &MergeRequestStore_SetRead_Call{Call: _e.mock.On("SetRead", ctx, repo, number, head)} | |||
| 572 | + | } | |||
| 573 | + | ||||
| 574 | + | func (_c *MergeRequestStore_SetRead_Call) Run(run func(ctx context.Context, repo domain.RepoName, number int64, head domain.ObjectID)) *MergeRequestStore_SetRead_Call { | |||
| 575 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 576 | + | var arg0 context.Context | |||
| 577 | + | if args[0] != nil { | |||
| 578 | + | arg0 = args[0].(context.Context) | |||
| 579 | + | } | |||
| 580 | + | var arg1 domain.RepoName | |||
| 581 | + | if args[1] != nil { | |||
| 582 | + | arg1 = args[1].(domain.RepoName) | |||
| 583 | + | } | |||
| 584 | + | var arg2 int64 | |||
| 585 | + | if args[2] != nil { | |||
| 586 | + | arg2 = args[2].(int64) | |||
| 587 | + | } | |||
| 588 | + | var arg3 domain.ObjectID | |||
| 589 | + | if args[3] != nil { | |||
| 590 | + | arg3 = args[3].(domain.ObjectID) | |||
| 591 | + | } | |||
| 592 | + | run( | |||
| 593 | + | arg0, | |||
| 594 | + | arg1, | |||
| 595 | + | arg2, | |||
| 596 | + | arg3, | |||
| 597 | + | ) | |||
| 598 | + | }) | |||
| 599 | + | return _c | |||
| 600 | + | } | |||
| 601 | + | ||||
| 602 | + | func (_c *MergeRequestStore_SetRead_Call) Return(err error) *MergeRequestStore_SetRead_Call { | |||
| 603 | + | _c.Call.Return(err) | |||
| 604 | + | return _c | |||
| 605 | + | } | |||
| 606 | + | ||||
| 607 | + | func (_c *MergeRequestStore_SetRead_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName, number int64, head domain.ObjectID) error) *MergeRequestStore_SetRead_Call { | |||
| 608 | + | _c.Call.Return(run) | |||
| 609 | + | return _c | |||
| 610 | + | } | |||
| 611 | + | ||||
| 612 | + | // Update provides a mock function for the type MergeRequestStore | |||
| 613 | + | func (_mock *MergeRequestStore) Update(ctx context.Context, mr domain.MergeRequest) error { | |||
| 614 | + | ret := _mock.Called(ctx, mr) | |||
| 615 | + | ||||
| 616 | + | if len(ret) == 0 { | |||
| 617 | + | panic("no return value specified for Update") | |||
| 618 | + | } | |||
| 619 | + | ||||
| 620 | + | var r0 error | |||
| 621 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.MergeRequest) error); ok { | |||
| 622 | + | r0 = returnFunc(ctx, mr) | |||
| 623 | + | } else { | |||
| 624 | + | r0 = ret.Error(0) | |||
| 625 | + | } | |||
| 626 | + | return r0 | |||
| 627 | + | } | |||
| 628 | + | ||||
| 629 | + | // MergeRequestStore_Update_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Update' | |||
| 630 | + | type MergeRequestStore_Update_Call struct { | |||
| 631 | + | *mock.Call | |||
| 632 | + | } | |||
| 633 | + | ||||
| 634 | + | // Update is a helper method to define mock.On call | |||
| 635 | + | // - ctx context.Context | |||
| 636 | + | // - mr domain.MergeRequest | |||
| 637 | + | func (_e *MergeRequestStore_Expecter) Update(ctx any, mr any) *MergeRequestStore_Update_Call { | |||
| 638 | + | return &MergeRequestStore_Update_Call{Call: _e.mock.On("Update", ctx, mr)} | |||
| 639 | + | } | |||
| 640 | + | ||||
| 641 | + | func (_c *MergeRequestStore_Update_Call) Run(run func(ctx context.Context, mr domain.MergeRequest)) *MergeRequestStore_Update_Call { | |||
| 642 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 643 | + | var arg0 context.Context | |||
| 644 | + | if args[0] != nil { | |||
| 645 | + | arg0 = args[0].(context.Context) | |||
| 646 | + | } | |||
| 647 | + | var arg1 domain.MergeRequest | |||
| 648 | + | if args[1] != nil { | |||
| 649 | + | arg1 = args[1].(domain.MergeRequest) | |||
| 650 | + | } | |||
| 651 | + | run( | |||
| 652 | + | arg0, | |||
| 653 | + | arg1, | |||
| 654 | + | ) | |||
| 655 | + | }) | |||
| 656 | + | return _c | |||
| 657 | + | } | |||
| 658 | + | ||||
| 659 | + | func (_c *MergeRequestStore_Update_Call) Return(err error) *MergeRequestStore_Update_Call { | |||
| 660 | + | _c.Call.Return(err) | |||
| 661 | + | return _c | |||
| 662 | + | } | |||
| 663 | + | ||||
| 664 | + | func (_c *MergeRequestStore_Update_Call) RunAndReturn(run func(ctx context.Context, mr domain.MergeRequest) error) *MergeRequestStore_Update_Call { | |||
| 665 | + | _c.Call.Return(run) | |||
| 666 | + | return _c | |||
| 667 | + | } | |||
M internal/ports/ports.go +81 -1
| @@ -28,7 +28,10 @@ type RepoMetaStore interface { | |||
| 28 | 28 | // there is none and domain.ErrConflict if another write changed the | |
| 29 | 29 | // stored repository since it was read. | |
| 30 | 30 | Update(ctx context.Context, repo domain.Repo) error | |
| 31 | - | // Delete removes the repository. Deleting a missing one is not an error. | |
| 31 | + | // Delete removes the repository and everything keyed to it: its merge | |
| 32 | + | // requests, their comments and read positions, and its number sequence. | |
| 33 | + | // A repository created again under the same name starts empty, with its | |
| 34 | + | // numbers starting at 1. Deleting a missing one is not an error. | |
| 32 | 35 | Delete(ctx context.Context, name domain.RepoName) error | |
| 33 | 36 | } | |
| 34 | 37 | ||
| @@ -185,6 +188,83 @@ type TokenStore interface { | |||
| 185 | 188 | Touch(ctx context.Context, id string, at time.Time) error | |
| 186 | 189 | } | |
| 187 | 190 | ||
| 191 | + | // MergeRequestStore keeps the merge requests of the repositories, their | |
| 192 | + | // comments and the read position of the reviewer. A repository has no entry | |
| 193 | + | // until its first merge request; the store does not check that it exists. | |
| 194 | + | type MergeRequestStore interface { | |
| 195 | + | // Create stores the merge request under the next number of its | |
| 196 | + | // repository, which it sets, and returns it. Numbers start at 1 and are | |
| 197 | + | // never reused, even by a concurrent Create. | |
| 198 | + | Create(ctx context.Context, mr domain.MergeRequest) (domain.MergeRequest, error) | |
| 199 | + | // Get returns the merge request, or domain.ErrNotFound. Its Version | |
| 200 | + | // tells Update which state it was read at. | |
| 201 | + | Get(ctx context.Context, repo domain.RepoName, number int64) (domain.MergeRequest, error) | |
| 202 | + | // List returns every merge request of the repository, highest number | |
| 203 | + | // first. | |
| 204 | + | List(ctx context.Context, repo domain.RepoName) ([]domain.MergeRequest, error) | |
| 205 | + | // Update replaces the stored merge request. It returns domain.ErrNotFound | |
| 206 | + | // if there is none and domain.ErrConflict if another write changed it | |
| 207 | + | // since mr was read. | |
| 208 | + | Update(ctx context.Context, mr domain.MergeRequest) error | |
| 209 | + | // ListComments returns the comments of the merge request, oldest first. | |
| 210 | + | ListComments(ctx context.Context, repo domain.RepoName, number int64) ([]domain.Comment, error) | |
| 211 | + | // GetComment returns one comment, or domain.ErrNotFound. | |
| 212 | + | GetComment(ctx context.Context, repo domain.RepoName, number int64, id int64) (domain.Comment, error) | |
| 213 | + | // SaveComment writes a comment and the merge request it belongs to in one | |
| 214 | + | // batch: a new comment (Version 0) must not exist yet, an existing one | |
| 215 | + | // must be unchanged since it was read, and so must mr. It returns | |
| 216 | + | // domain.ErrConflict otherwise. The caller sets mr.CommentSeq and the | |
| 217 | + | // comment's ID for a new comment, and mr.UpdatedAt. | |
| 218 | + | SaveComment(ctx context.Context, mr domain.MergeRequest, c domain.Comment) error | |
| 219 | + | // GetRead returns the head the reviewer last marked read, or the zero | |
| 220 | + | // ObjectID when there is none. | |
| 221 | + | GetRead(ctx context.Context, repo domain.RepoName, number int64) (domain.ObjectID, error) | |
| 222 | + | // SetRead records the head the reviewer has read. | |
| 223 | + | SetRead(ctx context.Context, repo domain.RepoName, number int64, head domain.ObjectID) error | |
| 224 | + | } | |
| 225 | + | ||
| 226 | + | // MergeGit is what merge requests need from the Git repositories beyond | |
| 227 | + | // RepoReader. Every method returns domain.ErrNotFound when the repository has | |
| 228 | + | // no directory. | |
| 229 | + | type MergeGit interface { | |
| 230 | + | // BranchHead returns the commit refs/heads/<branch> points to, or | |
| 231 | + | // domain.ErrNotFound when there is no such branch. | |
| 232 | + | BranchHead(ctx context.Context, repo domain.RepoName, branch string) (domain.ObjectID, error) | |
| 233 | + | // HasCommit reports whether the repository holds that commit. | |
| 234 | + | HasCommit(ctx context.Context, repo domain.RepoName, id domain.ObjectID) (bool, error) | |
| 235 | + | // MergeBase returns the best common ancestor of two commits, or | |
| 236 | + | // domain.ErrNotFound when they share no history. | |
| 237 | + | MergeBase(ctx context.Context, repo domain.RepoName, a, b domain.ObjectID) (domain.ObjectID, error) | |
| 238 | + | // IsAncestor reports whether ancestor is reachable from descendant, a | |
| 239 | + | // commit being its own ancestor. | |
| 240 | + | IsAncestor(ctx context.Context, repo domain.RepoName, ancestor, descendant domain.ObjectID) (bool, error) | |
| 241 | + | // ListRange returns the commits reachable from head and not from exclude | |
| 242 | + | // ("exclude..head"), newest first, at most limit; more is true when the | |
| 243 | + | // range holds more. A zero exclude means the whole history of head. | |
| 244 | + | ListRange(ctx context.Context, repo domain.RepoName, exclude, head domain.ObjectID, limit int) (commits []domain.Commit, more bool, err error) | |
| 245 | + | // DiffCommits returns the statistics and the diff from commit base to | |
| 246 | + | // commit head (renames detected), bounded by limits. | |
| 247 | + | DiffCommits(ctx context.Context, repo domain.RepoName, base, head domain.ObjectID, limits domain.DiffLimits) (domain.DiffStats, domain.Diff, error) | |
| 248 | + | // CommitTree returns the tree of a commit. | |
| 249 | + | CommitTree(ctx context.Context, repo domain.RepoName, id domain.ObjectID) (domain.ObjectID, error) | |
| 250 | + | // WriteCommit stores a commit object, given as the text of the object, | |
| 251 | + | // and returns its id. | |
| 252 | + | WriteCommit(ctx context.Context, repo domain.RepoName, object []byte) (domain.ObjectID, error) | |
| 253 | + | // UpdateBranch moves refs/heads/<branch> from old to new. It returns | |
| 254 | + | // domain.ErrConflict when the branch no longer points to old. | |
| 255 | + | UpdateBranch(ctx context.Context, repo domain.RepoName, branch string, old, new domain.ObjectID) error | |
| 256 | + | } | |
| 257 | + | ||
| 258 | + | // CommitSigner signs the commits ermite creates (ADR 0022). The private key | |
| 259 | + | // never leaves it. | |
| 260 | + | type CommitSigner interface { | |
| 261 | + | // PublicKey returns the public key and its fingerprint. | |
| 262 | + | PublicKey() domain.SigningPublicKey | |
| 263 | + | // Sign signs the text of a commit object and returns the armored SSH | |
| 264 | + | // signature (namespace "git") to put in its gpgsig header. | |
| 265 | + | Sign(payload []byte) (string, error) | |
| 266 | + | } | |
| 267 | + | ||
| 188 | 268 | // EventBus fans events out to in-process subscribers. | |
| 189 | 269 | type EventBus interface { | |
| 190 | 270 | // Publish hands the event to every subscriber without blocking. A | |
| @@ -28,7 +28,10 @@ type RepoMetaStore interface { | |||||
| 28 | // there is none and domain.ErrConflict if another write changed the | 28 | // there is none and domain.ErrConflict if another write changed the | ||
| 29 | // stored repository since it was read. | 29 | // stored repository since it was read. | ||
| 30 | Update(ctx context.Context, repo domain.Repo) error | 30 | Update(ctx context.Context, repo domain.Repo) error | ||
| 31 | - | // Delete removes the repository. Deleting a missing one is not an error. | 31 | + | // Delete removes the repository and everything keyed to it: its merge |
| 32 | + | // requests, their comments and read positions, and its number sequence. | |||
| 33 | + | // A repository created again under the same name starts empty, with its | |||
| 34 | + | // numbers starting at 1. Deleting a missing one is not an error. | |||
| 32 | Delete(ctx context.Context, name domain.RepoName) error | 35 | Delete(ctx context.Context, name domain.RepoName) error | ||
| 33 | } | 36 | } | ||
| 34 | 37 | ||||
| @@ -185,6 +188,83 @@ type TokenStore interface { | |||||
| 185 | Touch(ctx context.Context, id string, at time.Time) error | 188 | Touch(ctx context.Context, id string, at time.Time) error | ||
| 186 | } | 189 | } | ||
| 187 | 190 | ||||
| 191 | + | // MergeRequestStore keeps the merge requests of the repositories, their | |||
| 192 | + | // comments and the read position of the reviewer. A repository has no entry | |||
| 193 | + | // until its first merge request; the store does not check that it exists. | |||
| 194 | + | type MergeRequestStore interface { | |||
| 195 | + | // Create stores the merge request under the next number of its | |||
| 196 | + | // repository, which it sets, and returns it. Numbers start at 1 and are | |||
| 197 | + | // never reused, even by a concurrent Create. | |||
| 198 | + | Create(ctx context.Context, mr domain.MergeRequest) (domain.MergeRequest, error) | |||
| 199 | + | // Get returns the merge request, or domain.ErrNotFound. Its Version | |||
| 200 | + | // tells Update which state it was read at. | |||
| 201 | + | Get(ctx context.Context, repo domain.RepoName, number int64) (domain.MergeRequest, error) | |||
| 202 | + | // List returns every merge request of the repository, highest number | |||
| 203 | + | // first. | |||
| 204 | + | List(ctx context.Context, repo domain.RepoName) ([]domain.MergeRequest, error) | |||
| 205 | + | // Update replaces the stored merge request. It returns domain.ErrNotFound | |||
| 206 | + | // if there is none and domain.ErrConflict if another write changed it | |||
| 207 | + | // since mr was read. | |||
| 208 | + | Update(ctx context.Context, mr domain.MergeRequest) error | |||
| 209 | + | // ListComments returns the comments of the merge request, oldest first. | |||
| 210 | + | ListComments(ctx context.Context, repo domain.RepoName, number int64) ([]domain.Comment, error) | |||
| 211 | + | // GetComment returns one comment, or domain.ErrNotFound. | |||
| 212 | + | GetComment(ctx context.Context, repo domain.RepoName, number int64, id int64) (domain.Comment, error) | |||
| 213 | + | // SaveComment writes a comment and the merge request it belongs to in one | |||
| 214 | + | // batch: a new comment (Version 0) must not exist yet, an existing one | |||
| 215 | + | // must be unchanged since it was read, and so must mr. It returns | |||
| 216 | + | // domain.ErrConflict otherwise. The caller sets mr.CommentSeq and the | |||
| 217 | + | // comment's ID for a new comment, and mr.UpdatedAt. | |||
| 218 | + | SaveComment(ctx context.Context, mr domain.MergeRequest, c domain.Comment) error | |||
| 219 | + | // GetRead returns the head the reviewer last marked read, or the zero | |||
| 220 | + | // ObjectID when there is none. | |||
| 221 | + | GetRead(ctx context.Context, repo domain.RepoName, number int64) (domain.ObjectID, error) | |||
| 222 | + | // SetRead records the head the reviewer has read. | |||
| 223 | + | SetRead(ctx context.Context, repo domain.RepoName, number int64, head domain.ObjectID) error | |||
| 224 | + | } | |||
| 225 | + | ||||
| 226 | + | // MergeGit is what merge requests need from the Git repositories beyond | |||
| 227 | + | // RepoReader. Every method returns domain.ErrNotFound when the repository has | |||
| 228 | + | // no directory. | |||
| 229 | + | type MergeGit interface { | |||
| 230 | + | // BranchHead returns the commit refs/heads/<branch> points to, or | |||
| 231 | + | // domain.ErrNotFound when there is no such branch. | |||
| 232 | + | BranchHead(ctx context.Context, repo domain.RepoName, branch string) (domain.ObjectID, error) | |||
| 233 | + | // HasCommit reports whether the repository holds that commit. | |||
| 234 | + | HasCommit(ctx context.Context, repo domain.RepoName, id domain.ObjectID) (bool, error) | |||
| 235 | + | // MergeBase returns the best common ancestor of two commits, or | |||
| 236 | + | // domain.ErrNotFound when they share no history. | |||
| 237 | + | MergeBase(ctx context.Context, repo domain.RepoName, a, b domain.ObjectID) (domain.ObjectID, error) | |||
| 238 | + | // IsAncestor reports whether ancestor is reachable from descendant, a | |||
| 239 | + | // commit being its own ancestor. | |||
| 240 | + | IsAncestor(ctx context.Context, repo domain.RepoName, ancestor, descendant domain.ObjectID) (bool, error) | |||
| 241 | + | // ListRange returns the commits reachable from head and not from exclude | |||
| 242 | + | // ("exclude..head"), newest first, at most limit; more is true when the | |||
| 243 | + | // range holds more. A zero exclude means the whole history of head. | |||
| 244 | + | ListRange(ctx context.Context, repo domain.RepoName, exclude, head domain.ObjectID, limit int) (commits []domain.Commit, more bool, err error) | |||
| 245 | + | // DiffCommits returns the statistics and the diff from commit base to | |||
| 246 | + | // commit head (renames detected), bounded by limits. | |||
| 247 | + | DiffCommits(ctx context.Context, repo domain.RepoName, base, head domain.ObjectID, limits domain.DiffLimits) (domain.DiffStats, domain.Diff, error) | |||
| 248 | + | // CommitTree returns the tree of a commit. | |||
| 249 | + | CommitTree(ctx context.Context, repo domain.RepoName, id domain.ObjectID) (domain.ObjectID, error) | |||
| 250 | + | // WriteCommit stores a commit object, given as the text of the object, | |||
| 251 | + | // and returns its id. | |||
| 252 | + | WriteCommit(ctx context.Context, repo domain.RepoName, object []byte) (domain.ObjectID, error) | |||
| 253 | + | // UpdateBranch moves refs/heads/<branch> from old to new. It returns | |||
| 254 | + | // domain.ErrConflict when the branch no longer points to old. | |||
| 255 | + | UpdateBranch(ctx context.Context, repo domain.RepoName, branch string, old, new domain.ObjectID) error | |||
| 256 | + | } | |||
| 257 | + | ||||
| 258 | + | // CommitSigner signs the commits ermite creates (ADR 0022). The private key | |||
| 259 | + | // never leaves it. | |||
| 260 | + | type CommitSigner interface { | |||
| 261 | + | // PublicKey returns the public key and its fingerprint. | |||
| 262 | + | PublicKey() domain.SigningPublicKey | |||
| 263 | + | // Sign signs the text of a commit object and returns the armored SSH | |||
| 264 | + | // signature (namespace "git") to put in its gpgsig header. | |||
| 265 | + | Sign(payload []byte) (string, error) | |||
| 266 | + | } | |||
| 267 | + | ||||
| 188 | // EventBus fans events out to in-process subscribers. | 268 | // EventBus fans events out to in-process subscribers. | ||
| 189 | type EventBus interface { | 269 | type EventBus interface { | ||
| 190 | // Publish hands the event to every subscriber without blocking. A | 270 | // Publish hands the event to every subscriber without blocking. A | ||