Skip to content

Commit cba5a29

Browse files
Add spec tests for modelsdev, repoutil, semverutil (#43536)
1 parent 22976b0 commit cba5a29

3 files changed

Lines changed: 132 additions & 20 deletions

File tree

pkg/modelsdev/spec_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//go:build !integration
2+
3+
package modelsdev
4+
5+
import (
6+
"context"
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
// TestSpec_PublicAPI_FindPricing validates the documented behavior of
14+
// FindPricing as described in the modelsdev README.md specification.
15+
func TestSpec_PublicAPI_FindPricing(t *testing.T) {
16+
ctx := context.Background()
17+
18+
t.Run("returns nil false when pricing is unavailable", func(t *testing.T) {
19+
pricing, ok := FindPricing(ctx, "definitely-not-a-provider", "definitely-not-a-model")
20+
assert.False(t, ok, "FindPricing should report unavailable pricing for an unknown provider/model pair")
21+
assert.Nil(t, pricing, "FindPricing should return nil pricing when no pricing is available")
22+
})
23+
24+
t.Run("result pricing map exposes per token input and output entries when available", func(t *testing.T) {
25+
pricing, ok := FindPricing(ctx, "github", "gpt-4.1")
26+
if !ok {
27+
t.Skip("catalog pricing unavailable in test environment; README specifies graceful degradation when network or parsing fails")
28+
}
29+
30+
require.NotNil(t, pricing, "FindPricing should return a non-nil pricing map when pricing is available")
31+
_, hasInput := pricing["input"]
32+
_, hasOutput := pricing["output"]
33+
assert.True(t, hasInput, "pricing map should contain documented input per-token price entry")
34+
assert.True(t, hasOutput, "pricing map should contain documented output per-token price entry")
35+
})
36+
}
37+
38+
// TestSpec_DesignDecision_ProviderAliases validates the documented provider
39+
// alias normalization described in the modelsdev README.md.
40+
func TestSpec_DesignDecision_ProviderAliases(t *testing.T) {
41+
tests := []struct {
42+
name string
43+
provider string
44+
}{
45+
{name: "github alias", provider: "github"},
46+
{name: "copilot alias", provider: "copilot"},
47+
{name: "github_models alias", provider: "github_models"},
48+
}
49+
50+
for _, tt := range tests {
51+
t.Run(tt.name, func(t *testing.T) {
52+
pricing, ok := FindPricing(context.Background(), tt.provider, "definitely-not-a-real-model")
53+
assert.False(t, ok, "FindPricing should still return unavailable for an unknown model after normalizing provider alias %q", tt.provider)
54+
assert.Nil(t, pricing, "FindPricing should return nil pricing for unknown model with provider alias %q", tt.provider)
55+
})
56+
}
57+
}

pkg/repoutil/spec_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,35 @@ func TestSpec_PublicAPI_SplitRepoSlug(t *testing.T) {
6363
})
6464
}
6565
}
66+
67+
// TestSpec_PublicAPI_NormalizeRepoForAPI validates the documented behavior of
68+
// NormalizeRepoForAPI as described in the repoutil README.md specification.
69+
func TestSpec_PublicAPI_NormalizeRepoForAPI(t *testing.T) {
70+
tests := []struct {
71+
name string
72+
input string
73+
expectedOwnerRepo string
74+
expectedHost string
75+
}{
76+
{
77+
name: "plain owner repo keeps empty host",
78+
input: "github/gh-aw",
79+
expectedOwnerRepo: "github/gh-aw",
80+
expectedHost: "",
81+
},
82+
{
83+
name: "host owner repo splits into owner repo and host",
84+
input: "ghe.example.com/github/gh-aw",
85+
expectedOwnerRepo: "github/gh-aw",
86+
expectedHost: "ghe.example.com",
87+
},
88+
}
89+
90+
for _, tt := range tests {
91+
t.Run(tt.name, func(t *testing.T) {
92+
ownerRepo, host := NormalizeRepoForAPI(tt.input)
93+
assert.Equal(t, tt.expectedOwnerRepo, ownerRepo, "owner/repo mismatch for input %q", tt.input)
94+
assert.Equal(t, tt.expectedHost, host, "host mismatch for input %q", tt.input)
95+
})
96+
}
97+
}

pkg/semverutil/spec_test.go

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -125,14 +125,12 @@ func TestSpec_PublicAPI_ParseVersion(t *testing.T) {
125125
})
126126

127127
t.Run("Pre field is prerelease identifier without leading hyphen", func(t *testing.T) {
128-
// Spec: Pre string // Prerelease identifier without leading hyphen (e.g. "beta.1")
129128
ver := ParseVersion("v1.2.3-beta.1")
130129
require.NotNil(t, ver, "ParseVersion should return non-nil for valid prerelease semver")
131130
assert.Equal(t, "beta.1", ver.Pre, "Pre field should contain prerelease identifier without leading hyphen")
132131
})
133132

134133
t.Run("Raw field is original version string without leading v prefix", func(t *testing.T) {
135-
// Spec: Raw string // Original version string without leading "v"
136134
ver := ParseVersion("v1.2.3")
137135
require.NotNil(t, ver, "ParseVersion should return non-nil for valid semver")
138136
assert.Equal(t, "1.2.3", ver.Raw, "Raw field should contain version string without leading v prefix")
@@ -216,6 +214,12 @@ func TestSpec_PublicAPI_Compare(t *testing.T) {
216214
v2: "v1.0.0",
217215
expected: -1,
218216
},
217+
{
218+
name: "bare versions without v prefix are accepted",
219+
v1: "2.0.0",
220+
v2: "1.9.9",
221+
expected: 1,
222+
},
219223
}
220224

221225
for _, tt := range tests {
@@ -226,6 +230,43 @@ func TestSpec_PublicAPI_Compare(t *testing.T) {
226230
}
227231
}
228232

233+
// TestSpec_PublicAPI_IsMorePreciseVersion validates the documented behavior of
234+
// IsMorePreciseVersion as described in the semverutil README.md specification.
235+
func TestSpec_PublicAPI_IsMorePreciseVersion(t *testing.T) {
236+
tests := []struct {
237+
name string
238+
v1 string
239+
v2 string
240+
expected bool
241+
}{
242+
{
243+
name: "more specific version sorts ahead of less specific version",
244+
v1: "v4.3.0",
245+
v2: "v4",
246+
expected: true,
247+
},
248+
{
249+
name: "less specific version does not sort ahead of more specific version",
250+
v1: "v4",
251+
v2: "v4.3.0",
252+
expected: false,
253+
},
254+
{
255+
name: "equal precision and equal value returns false",
256+
v1: "v4.3.0",
257+
v2: "v4.3.0",
258+
expected: false,
259+
},
260+
}
261+
262+
for _, tt := range tests {
263+
t.Run(tt.name, func(t *testing.T) {
264+
result := IsMorePreciseVersion(tt.v1, tt.v2)
265+
assert.Equal(t, tt.expected, result, "IsMorePreciseVersion(%q, %q) mismatch", tt.v1, tt.v2)
266+
})
267+
}
268+
}
269+
229270
// TestSpec_PublicAPI_IsCompatible validates the documented behavior of
230271
// IsCompatible as described in the semverutil README.md specification.
231272
func TestSpec_PublicAPI_IsCompatible(t *testing.T) {
@@ -259,24 +300,6 @@ func TestSpec_PublicAPI_IsCompatible(t *testing.T) {
259300
requestedVersion: "also-not-a-version",
260301
expected: false,
261302
},
262-
{
263-
name: "empty strings are not compatible",
264-
pinVersion: "",
265-
requestedVersion: "",
266-
expected: false,
267-
},
268-
{
269-
name: "invalid pin with valid requested is not compatible",
270-
pinVersion: "not-a-version",
271-
requestedVersion: "v5.0.0",
272-
expected: false,
273-
},
274-
{
275-
name: "valid pin with invalid requested is not compatible",
276-
pinVersion: "v5.0.0",
277-
requestedVersion: "not-a-version",
278-
expected: false,
279-
},
280303
}
281304

282305
for _, tt := range tests {

0 commit comments

Comments
 (0)