1package agent
2
3import (
4 "context"
5 "errors"
6 "net/http"
7 "os"
8 "path/filepath"
9 "testing"
10)
11
12func TestIsDevVersion(t *testing.T) {
13 for _, version := range []string{"", "dev", "0.0.0", "0.0.0-dev", "0.0.0-dev (abc123, today)", "0.0.0-dev+local"} {
14 if !isDevVersion(version) {
15 t.Errorf("isDevVersion(%q) = false, want true", version)
16 }
17 }
18 for _, version := range []string{"0.0.1", "1.0.0", "0.0.0-rc1"} {
19 if isDevVersion(version) {
20 t.Errorf("isDevVersion(%q) = true, want false", version)
21 }
22 }
23}
24
25type countingRoundTripper struct {
26 calls int
27}
28
29func (r *countingRoundTripper) RoundTrip(*http.Request) (*http.Response, error) {
30 r.calls++
31 return nil, errors.New("unexpected network request")
32}
33
34func TestCheckForUpdateSkipsDevVersionWithoutCacheOrNetwork(t *testing.T) {
35 home := t.TempDir()
36 transport := &countingRoundTripper{}
37 previousTransport := http.DefaultTransport
38 http.DefaultTransport = transport
39 t.Cleanup(func() { http.DefaultTransport = previousTransport })
40
41 ctx, cancel := context.WithCancel(context.Background())
42 cancel()
43
44 info := CheckForUpdate(ctx, home, "0.0.0-dev")
45 if info != (UpdateInfo{}) {
46 t.Fatalf("development check = %+v, want an empty result", info)
47 }
48 if _, err := os.Stat(filepath.Join(home, updateCheckFile)); !os.IsNotExist(err) {
49 t.Fatalf("development check wrote a cache file: %v", err)
50 }
51 if transport.calls != 0 {
52 t.Fatalf("development check made %d network requests, want 0", transport.calls)
53 }
54}
55
56func TestVersionLess(t *testing.T) {
57 pseudo := "0.3.35-0.20260809165004-9e3c28d4b65"
58 cases := []struct {
59 name string
60 a, b string
61 want bool
62 }{
63 {name: "older release", a: "0.3.33", b: "0.3.34", want: true},
64 {name: "newer release", a: "0.3.35", b: "0.3.34", want: false},
65 {name: "pseudo after prior release", a: pseudo, b: "0.3.34", want: false},
66 {name: "pseudo before final release", a: pseudo, b: "0.3.35", want: true},
67 {name: "prior release before pseudo", a: "0.3.34", b: pseudo, want: true},
68 {name: "decorated build", a: "0.3.33 (abc1234, 2026-08-09)", b: "0.3.34", want: true},
69 {name: "invalid current", a: "not-a-version", b: "0.3.34", want: false},
70 }
71 for _, tc := range cases {
72 t.Run(tc.name, func(t *testing.T) {
73 if got := versionLess(tc.a, tc.b); got != tc.want {
74 t.Fatalf("versionLess(%q, %q) = %v, want %v", tc.a, tc.b, got, tc.want)
75 }
76 })
77 }
78}
79
80func TestBuildInfoDoesNotOfferPseudoVersionDowngrade(t *testing.T) {
81 current := "0.3.35-0.20260809165004-9e3c28d4b65"
82 info := buildInfo(current, "v0.3.34", "https://example.com/release")
83 if info.Available {
84 t.Fatalf("buildInfo(%q, %q) offered a downgrade", current, info.Latest)
85 }
86}