Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,10 @@
"end": {
"type": "integer",
"description": "End line number"
},
"attribution": {
"type": "string",
Comment thread
jiparis marked this conversation as resolved.
"description": "Owner of the lines in the range: ai or human. A range with an attribution is a position in the file at the pushed commit. A range with no attribution is an AI range at edit time."
}
},
"additionalProperties": false
Expand Down
50 changes: 50 additions & 0 deletions internal/schemavalidators/schemavalidators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,56 @@ func TestValidateAICodingSessionSpec(t *testing.T) {
}
}

// TestValidateAICodingSessionLineRanges covers code_changes.files[].line_ranges.
// A range can say if its lines are AI or human (spec issue-3639, line ranges
// D-005). A range with no attribution keeps its earlier meaning, so the
// property is optional.
func TestValidateAICodingSessionLineRanges(t *testing.T) {
withRange := func(t *testing.T, r map[string]any) map[string]any {
t.Helper()
f, err := os.ReadFile("./testdata/ai_coding_session_valid.json")
require.NoError(t, err)

var payload map[string]any
require.NoError(t, json.Unmarshal(f, &payload))
payload["code_changes"] = map[string]any{
"files": []any{
map[string]any{"path": "main.go", "status": "modified", "line_ranges": []any{r}},
},
}
return payload
}

const (
keyStart = "start"
keyEnd = "end"
keyAttribution = "attribution"
)

testCases := []struct {
name string
r map[string]any
wantErr bool
}{
{name: "accepts a range with no attribution", r: map[string]any{keyStart: 1, keyEnd: 3}},
{name: "accepts an AI range", r: map[string]any{keyStart: 1, keyEnd: 3, keyAttribution: "ai"}},
{name: "accepts a human range", r: map[string]any{keyStart: 4, keyEnd: 4, keyAttribution: "human"}},
{name: "rejects a non-string attribution", r: map[string]any{keyStart: 1, keyEnd: 3, keyAttribution: 1}, wantErr: true},
{name: "rejects an unknown property", r: map[string]any{keyStart: 1, keyEnd: 3, "owner": "ai"}, wantErr: true},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := schemavalidators.ValidateAICodingSession(withRange(t, tc.r), "")
if tc.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
})
}
}

func TestValidateSecurityContext(t *testing.T) {
testCases := []struct {
name string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,10 @@ type GitContext struct {
type LineRange struct {
Start int `json:"start"`
End int `json:"end"`
// Attribution is the owner of the lines, ai or human. A range with an
// attribution is a position in the file at the pushed commit. A range
// with no attribution is an AI range at edit time, as older CLIs write it.
Attribution string `json:"attribution,omitempty"`
}

// FileChange represents a single file modification in the session.
Expand Down
Loading