-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathcommitrequest_attribution_agreement_test.go
More file actions
152 lines (136 loc) · 6.14 KB
/
Copy pathcommitrequest_attribution_agreement_test.go
File metadata and controls
152 lines (136 loc) · 6.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// SPDX-License-Identifier: Apache-2.0
package controller
import (
"context"
"testing"
"time"
"github.com/go-logr/logr"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
k8stypes "k8s.io/apimachinery/pkg/types"
configv1alpha3 "github.com/ConfigButler/gitops-reverser/api/v1alpha3"
"github.com/ConfigButler/gitops-reverser/internal/git"
"github.com/ConfigButler/gitops-reverser/internal/queue"
"github.com/ConfigButler/gitops-reverser/internal/watch"
)
// absentLookup is an attribution index that never has a fact — the production shape of
// "attribution is enabled but nothing matched within the grace".
type absentLookup struct{}
func (absentLookup) Await(
_ context.Context,
_ queue.FactQuery,
_ time.Duration,
) queue.AuthorResolution {
return queue.AuthorResolution{Result: queue.AttributionAbsent}
}
// windowOutcomeAttributionOff is the outcome a commit window carries in configured-author mode.
// It is read off a zero git.Event on purpose rather than written as a constant: that IS the
// production value, because watch.Manager.attachAuthor returns early without assigning
// Attribution when the Manager's AuthorResolver is nil.
func windowOutcomeAttributionOff() git.AttributionOutcome {
return git.Event{}.Attribution
}
// windowOutcomeAttributionMissed drives the real resolver over a lookup that has no fact, which
// is what a live watch event gets when attribution is on and nothing matched in the grace.
func windowOutcomeAttributionMissed(t *testing.T) git.AttributionOutcome {
t.Helper()
_, outcome := watch.NewAuthorResolver(absentLookup{}, 0, logr.Discard()).ResolveAuthor(
context.Background(),
watch.AuthorQuery{
AuditRoute: "default",
GVR: schema.GroupVersionResource{Version: "v1", Resource: "configmaps"},
UID: k8stypes.UID("uid-1"),
ResourceVersion: "101",
ExactCapable: true,
},
)
return outcome
}
// TestCommitRequest_OutcomesAgree is the cross-subsystem half of the P0 regression test.
//
// The gap that let the blocker through was that every existing test set both sides of the
// comparison to the same literal, so nothing ever checked that the two INDEPENDENT producers
// emit compatible values. This drives each side from its real source — the controller's
// gitOutcome projection, and the watch package's actual resolver / actual zero event — and
// asserts they agree on the only thing matching may depend on across that boundary.
//
// The default deployment is the first row: --admission-webhook defaults to false, so the
// controller emits attributionNotAttempted, while --author-attribution=false leaves the window's
// outcome at the zero value. When those two were different strings, no CommitRequest could
// attach to any window and the user's commit message was silently dropped.
func TestCommitRequest_OutcomesAgree(t *testing.T) {
tests := []struct {
name string
requestSide commitRequestAttribution
windowSide git.AttributionOutcome
wantNamesActor bool
}{
{
name: "default deployment: webhook off, attribution off",
requestSide: attributionNotAttempted,
windowSide: windowOutcomeAttributionOff(),
},
{
name: "webhook off, attribution on but missed",
requestSide: attributionNotAttempted,
windowSide: windowOutcomeAttributionMissed(t),
},
{
name: "webhook on but no record, attribution off",
requestSide: attributionCommitter,
windowSide: windowOutcomeAttributionOff(),
},
{
name: "webhook on but no record, attribution on but missed",
requestSide: attributionCommitter,
windowSide: windowOutcomeAttributionMissed(t),
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
requestOutcome := tc.requestSide.gitOutcome()
assert.Equal(t, tc.wantNamesActor, requestOutcome.NamesActor())
assert.Equal(t, requestOutcome.NamesActor(), tc.windowSide.NamesActor(),
"the CommitRequest side produced %q and the window side produced %q; they must "+
"agree on whether an actor was named, or the request cannot attach and the "+
"user's commit message is silently dropped",
requestOutcome, tc.windowSide)
})
}
}
// TestAttributionOutcome_OffLeavesZeroOutcome states the invariant the default deployment rests on,
// separately from the agreement table so a regression names itself precisely.
func TestAttributionOutcome_OffLeavesZeroOutcome(t *testing.T) {
assert.Equal(t, git.AttributionNotAttempted, windowOutcomeAttributionOff(),
"configured-author mode must leave the event's outcome at AttributionNotAttempted")
assert.Equal(t, git.AttributionNotAttempted, attributionNotAttempted.gitOutcome(),
"a CommitRequest with command-author capture off must carry AttributionNotAttempted")
assert.Equal(t, git.AttributionUnresolved, windowOutcomeAttributionMissed(t),
"attribution that ran and found nothing must be AttributionUnresolved, not the zero value")
}
// TestCommitRequestAttribution_GitOutcome keeps every controller outcome's projection explicit,
// including the defensive zero value.
func TestCommitRequestAttribution_GitOutcome(t *testing.T) {
tests := []struct {
name string
input commitRequestAttribution
want git.AttributionOutcome
}{
{name: "unset is not attempted", input: attributionUnset, want: git.AttributionNotAttempted},
{name: "admission capture is resolved", input: attributionFromAdmission, want: git.AttributionResolved},
{name: "admission miss is unresolved", input: attributionCommitter, want: git.AttributionUnresolved},
{name: "capture disabled is not attempted", input: attributionNotAttempted, want: git.AttributionNotAttempted},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
assert.Equal(t, tc.want, tc.input.gitOutcome())
})
}
}
// TestCommitRequestAttribution_UnsetStatusIsCaptureDisabled keeps an unset decision visible and safe.
func TestCommitRequestAttribution_UnsetStatusIsCaptureDisabled(t *testing.T) {
var request configv1alpha3.CommitRequest
setCommitRequestAttributed(&request, attributionUnset)
requireCondition(t, request, ConditionTypeAuthorAttributed, metav1.ConditionFalse, crReasonAuthorCaptureDisabled)
}