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
33 changes: 15 additions & 18 deletions scripts/collectlogs/collectlogs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,25 +296,22 @@ func TestIsHostname_RejectsNonHosts(t *testing.T) {
}

func TestClassifyIP_DistinguishesPrivatePublicAndLoopbackAddresses(t *testing.T) {
cases := map[string]struct {
class string
keep bool
}{
"127.0.0.1": {"loopback", true},
"::1": {"loopback", true},
"0.0.0.0": {"loopback", true},
"192.168.1.10": {"lan", false},
"10.221.6.52": {"lan", false},
"172.16.4.4": {"lan", false},
"100.64.0.1": {"cgnat", false},
"8.8.8.8": {"public", false},
"169.254.10.10": {"link-local", true},
}
for input, want := range cases {
class, keep := classifyIP(input)
assert.Equal(t, want.class, class, "IP %q", input)
assert.Equal(t, want.keep, keep, "IP %q", input)
test := func(name, input, wantClass string, wantKeep bool) {
t.Run(name, func(t *testing.T) {
class, keep := classifyIP(input)
assert.Equal(t, wantClass, class)
assert.Equal(t, wantKeep, keep)
})
}
test("IPv4 loopback", "127.0.0.1", "loopback", true)
test("IPv6 loopback", "::1", "loopback", true)
test("unspecified", "0.0.0.0", "loopback", true)
test("192.168 LAN", "192.168.1.10", "lan", false)
test("10 LAN", "10.221.6.52", "lan", false)
test("172.16 LAN", "172.16.4.4", "lan", false)
test("carrier-grade NAT", "100.64.0.1", "cgnat", false)
test("public", "8.8.8.8", "public", false)
test("link-local", "169.254.10.10", "link-local", true)
}

// Tokens must be identical across runs on the same input, otherwise two
Expand Down
70 changes: 44 additions & 26 deletions scripts/inference-dispatcher/dispatcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,19 @@ func configuredTestClient(t *testing.T, args []string, baseURL string, stderr *b
return cfg, client
}

// The inventory puts an embedding model first and chat models out of order.
// Default selection must find a generation-capable model rather than use list order.
func TestOmittedModel_SelectsAvailableGenerationModel(t *testing.T) {
var receivedModel string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api/tags":
_, _ = w.Write([]byte(`{"models":[
_, err := w.Write([]byte(`{"models":[
{"name":"z-embed","capabilities":["embedding"]},
{"name":"b-chat","capabilities":["chat"]},
{"name":"a-completion","capabilities":["completion"]}
]}`))
assert.NoError(t, err, "write engine response")
case "/api/generate":
var request struct {
Model string `json:"model"`
Expand All @@ -69,7 +72,8 @@ func TestOmittedModel_SelectsAvailableGenerationModel(t *testing.T) {
return
}
receivedModel = request.Model
_, _ = w.Write([]byte(`{"response":"hello"}`))
_, err := w.Write([]byte(`{"response":"hello"}`))
assert.NoError(t, err, "write engine response")
default:
http.NotFound(w, r)
}
Expand All @@ -96,7 +100,8 @@ func TestExplicitModel_SkipsInventoryRequest(t *testing.T) {
assert.Fail(t, "explicit model unexpectedly queried inventory")
return
}
_, _ = w.Write([]byte(`{"response":"ok"}`))
_, err := w.Write([]byte(`{"response":"ok"}`))
assert.NoError(t, err, "write engine response")
}))
defer server.Close()

Expand All @@ -118,9 +123,11 @@ func TestLMStudio_FallsBackToOpenAIInventory(t *testing.T) {
case "/api/v1/models":
http.Error(w, "not found", http.StatusNotFound)
case "/v1/models":
_, _ = w.Write([]byte(`{"data":[{"id":"live-model"}]}`))
_, err := w.Write([]byte(`{"data":[{"id":"live-model"}]}`))
assert.NoError(t, err, "write engine response")
case "/v1/chat/completions":
_, _ = w.Write([]byte(`{"choices":[{"message":{"content":"done"}}]}`))
_, err := w.Write([]byte(`{"choices":[{"message":{"content":"done"}}]}`))
assert.NoError(t, err, "write engine response")
default:
http.NotFound(w, r)
}
Expand All @@ -140,6 +147,8 @@ func TestLMStudio_FallsBackToOpenAIInventory(t *testing.T) {
assert.Contains(t, stdout.String(), "live-model", "selected model missing from output")
}

// llama.cpp exposes OpenAI inventory and chat routes rather than Ollama's native
// API. Auto-selection and dispatch must agree on that protocol.
func TestLlamaCPP_UsesOpenAIInventoryAndChat(t *testing.T) {
type observedRequest struct {
method string
Expand All @@ -152,7 +161,8 @@ func TestLlamaCPP_UsesOpenAIInventoryAndChat(t *testing.T) {
switch {
case r.Method == http.MethodGet && r.URL.Path == "/v1/models":
observed <- observedRequest{method: r.Method, path: r.URL.Path}
_, _ = w.Write([]byte(`{"data":[{"id":"llama-demo"}]}`))
_, err := w.Write([]byte(`{"data":[{"id":"llama-demo"}]}`))
assert.NoError(t, err)
case r.Method == http.MethodPost && r.URL.Path == "/v1/chat/completions":
var request struct {
Model string `json:"model"`
Expand All @@ -168,7 +178,8 @@ func TestLlamaCPP_UsesOpenAIInventoryAndChat(t *testing.T) {
model: request.Model,
messageCount: len(request.Messages),
}
_, _ = w.Write([]byte(`{"choices":[{"message":{"content":"done"}}]}`))
_, err := w.Write([]byte(`{"choices":[{"message":{"content":"done"}}]}`))
assert.NoError(t, err)
default:
http.NotFound(w, r)
}
Expand Down Expand Up @@ -199,16 +210,18 @@ func TestLMStudio_PrefersAggregatedInventory(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/v1/models":
_, _ = w.Write([]byte(`{"data":[
_, err := w.Write([]byte(`{"data":[
{"id":"remote-chat"},
{"id":"local-embed"},
{"id":"local-chat"}
]}`))
assert.NoError(t, err, "write engine response")
case "/api/v1/models":
_, _ = w.Write([]byte(`{"models":[
_, err := w.Write([]byte(`{"models":[
{"key":"local-embed","type":"embeddings"},
{"key":"local-chat","type":"llm"}
]}`))
assert.NoError(t, err, "write engine response")
default:
http.NotFound(w, r)
}
Expand Down Expand Up @@ -242,7 +255,8 @@ func TestLMStudio_PrefersAggregatedInventory(t *testing.T) {

func TestListModels_EmitsJSON(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte(`{"models":[{"name":"test-model"}]}`))
_, err := w.Write([]byte(`{"models":[{"name":"test-model"}]}`))
assert.NoError(t, err, "write engine response")
}))
defer server.Close()

Expand Down Expand Up @@ -281,7 +295,8 @@ func TestCancellation_StopsInFlightRequestCleanly(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api/tags":
_, _ = w.Write([]byte(`{"models":[{"name":"available"}]}`))
_, err := w.Write([]byte(`{"models":[{"name":"available"}]}`))
assert.NoError(t, err, "write engine response")
case "/api/generate":
once.Do(func() { close(requestStarted) })
<-releaseServer
Expand Down Expand Up @@ -350,9 +365,11 @@ func TestResponseText_NeverReachesStdout(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api/tags":
_, _ = w.Write([]byte(`{"models":[{"name":"available"}]}`))
_, err := w.Write([]byte(`{"models":[{"name":"available"}]}`))
assert.NoError(t, err, "write engine response")
case "/api/generate":
_, _ = w.Write([]byte(`{"response":"` + secret + `"}`))
_, err := w.Write([]byte(`{"response":"` + secret + `"}`))
assert.NoError(t, err, "write engine response")
default:
http.NotFound(w, r)
}
Expand All @@ -376,7 +393,8 @@ func TestUpstreamErrorBody_NeverReachesLogs(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api/tags":
_, _ = w.Write([]byte(`{"models":[{"name":"available"}]}`))
_, err := w.Write([]byte(`{"models":[{"name":"available"}]}`))
assert.NoError(t, err, "write engine response")
default:
http.Error(w, `{"error":{"message":"`+echoed+`"}}`, http.StatusBadRequest)
}
Expand Down Expand Up @@ -404,19 +422,19 @@ func TestUpstreamErrorBody_NeverReachesLogs(t *testing.T) {
require.NoError(t, err, "read result log")
errors, err := os.ReadFile(errorLog)
require.NoError(t, err, "read error log")
for name, content := range map[string]string{
"stdout": stdout.String(),
"stderr": stderr.String(),
"result log": string(results),
"error log": string(errors),
} {
assert.NotContains(t, content, "quarterly", "%s leaked the upstream error body", name)
assert.NotContains(t, content, echoed, "%s leaked the upstream error body", name)
if name == "stdout" {
continue
}
assert.Contains(t, content, "400 Bad Request", "%s dropped the HTTP status", name)
test := func(name, content string, wantStatus bool) {
t.Run(name, func(t *testing.T) {
assert.NotContains(t, content, "quarterly", "upstream error body must not leak")
assert.NotContains(t, content, echoed, "upstream error body must not leak")
if wantStatus {
assert.Contains(t, content, "400 Bad Request", "HTTP status must be preserved")
}
})
}
test("stdout", stdout.String(), false)
test("stderr", stderr.String(), true)
test("result log", string(results), true)
test("error log", string(errors), true)
}

func TestPromptDigest_DoesNotLeakPromptText(t *testing.T) {
Expand Down
35 changes: 22 additions & 13 deletions services/eap-noob/eapnoob_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,15 @@ func TestPairing_DerivesMatchingSecretsAcrossSuitesAndDirections(t *testing.T) {
assert.Equal(t, pSecret, sSecret, "exported secret mismatch at length %d", n)
}

a, _ := srv.Export("label-a", nil, 32)
b, _ := srv.Export("label-b", nil, 32)
a, err := srv.Export("label-a", nil, 32)
require.NoError(t, err, "export label-a")
b, err := srv.Export("label-b", nil, 32)
require.NoError(t, err, "export label-b")
assert.NotEqual(t, b, a, "different labels produced identical secrets")
c1, _ := srv.Export("app", []byte("ctx1"), 32)
c2, _ := srv.Export("app", []byte("ctx2"), 32)
c1, err := srv.Export("app", []byte("ctx1"), 32)
require.NoError(t, err, "export ctx1")
c2, err := srv.Export("app", []byte("ctx2"), 32)
require.NoError(t, err, "export ctx2")
assert.NotEqual(t, c2, c1, "different contexts produced identical secrets")
})
}
Expand All @@ -126,7 +130,8 @@ func TestPairing_RejectsTamperedMAC(t *testing.T) {

// Run the Completion handshake manually and corrupt MACp before the server
// verifies it.
req1, _ := srv.Start()
req1, err := srv.Start()
require.NoError(t, err, "start completion discovery")
resp1, err := peer.Receive(req1) // Type=1 response
require.NoError(t, err, "peer discovery:")
req6, err := srv.Receive(resp1.Send) // Type=6 request (MACs)
Expand Down Expand Up @@ -180,13 +185,17 @@ func TestOneStepKDF_DerivesRepeatableKeys(t *testing.T) {
}

func TestJWK_RoundTripsPublicKeys(t *testing.T) {
for _, id := range []int{1, 2} {
cs, err := suiteByID(id)
require.NoError(t, err)
priv, jwk, err := cs.generateKeypair()
require.NoError(t, err, "generate:")
pub, err := cs.decodeJWK(jwk)
require.NoError(t, err, "decode JWK:")
assert.Equal(t, priv.PublicKey().Bytes(), pub.Bytes(), "suite %d: JWK round-trip mismatch", id)
test := func(name string, id int) {
t.Run(name, func(t *testing.T) {
cs, err := suiteByID(id)
require.NoError(t, err)
priv, jwk, err := cs.generateKeypair()
require.NoError(t, err, "generate keypair")
pub, err := cs.decodeJWK(jwk)
require.NoError(t, err, "decode JWK")
assert.Equal(t, priv.PublicKey().Bytes(), pub.Bytes(), "JWK round-trip mismatch")
})
}
test("suite 1", 1)
test("suite 2", 2)
}
10 changes: 8 additions & 2 deletions services/eap-noob/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,14 @@ func Example() {

// Both sides now share an association and can derive a secret of any length.
const secretLen = 48
serverSecret, _ := srv.Export("my-application", nil, secretLen)
peerSecret, _ := peer.Export("my-application", nil, secretLen)
serverSecret, err := srv.Export("my-application", nil, secretLen)
if err != nil {
panic(err)
}
peerSecret, err := peer.Export("my-application", nil, secretLen)
if err != nil {
panic(err)
}

fmt.Println("registered:", srv.State() == eapnoob.StateRegistered)
fmt.Println("secret length:", len(serverSecret))
Expand Down
5 changes: 4 additions & 1 deletion services/nvpair-cluster-manager/admission_migration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,14 @@ func TestLegacyPairingInfo_MapsToFirstAdmission(t *testing.T) {

info.AdmissionEpoch = 0
info.V = pairingInfoVersion
raw, _ = json.Marshal(info)
raw, err = json.Marshal(info)
require.NoError(t, err)
_, _, err = parsePairingInfo(raw)
require.Error(t, err, "v2 pairing info without admission epoch was accepted")
}

// Old persisted pins lack admission metadata. Restart must bind them to the
// current cluster so existing members remain trusted and removable while offline.
func TestRestart_MigratesLegacyPinnedMemberAdmission(t *testing.T) {
dir := t.TempDir()
m := testManagerAt(t, dir, 15124)
Expand Down
20 changes: 12 additions & 8 deletions services/nvpair-cluster-manager/cm_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,19 @@ func TestIdentity_RejectsReloadWhenPrivateKeyIsMissing(t *testing.T) {
}

func TestPIN_RoundTripsThroughNoobEncoding(t *testing.T) {
cases := []string{"000000", "000123", "402199", "999999"}
for _, pin := range cases {
noob := noobFromPIN(pin)
require.Len(t, noob, 16, "noob length")
got := new(big.Int).SetBytes(noob).String()
want := new(big.Int)
want.SetString(pin, 10)
require.Equal(t, want.String(), got, "noob decodes incorrectly")
test := func(name, pin string) {
t.Run(name, func(t *testing.T) {
noob := noobFromPIN(pin)
require.Len(t, noob, 16, "noob length")
want := new(big.Int)
want.SetString(pin, 10)
require.Equal(t, want.String(), new(big.Int).SetBytes(noob).String(), "noob decodes incorrectly")
})
}
test("all zeroes", "000000")
test("leading zeroes", "000123")
test("ordinary PIN", "402199")
test("maximum PIN", "999999")

gp, noob, err := generatePIN()
require.NoError(t, err, "generatePIN")
Expand Down
2 changes: 1 addition & 1 deletion services/nvpair-cluster-manager/connreuse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func TestReconcile_ReusesPeerConnections(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc(rosterPath, func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"clusterId":"cluster-1"}`))
writeTestResponse(t, w, []byte(`{"clusterId":"cluster-1"}`))
})
ts := httptest.NewUnstartedServer(mux)
ts.Config.ConnState = func(_ net.Conn, state http.ConnState) {
Expand Down
13 changes: 9 additions & 4 deletions services/nvpair-cluster-manager/fsutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,13 @@ func TestAtomicWrite_FailsImmediatelyForPermanentRenameError(t *testing.T) {
}

func TestIsTransientReplaceError_RecognizesRetryableRenameFailures(t *testing.T) {
require.True(t, isTransientReplaceError(errors.New("Access is denied.")), "expected Access is denied to be transient")
require.True(t, isTransientReplaceError(errors.New("The process cannot access the file because it is being used by another process.")), "expected sharing text to be transient")
require.False(t, isTransientReplaceError(errors.New("no such file or directory")), "ENOENT must not be treated as transient")
require.False(t, isTransientReplaceError(nil), "nil must not be transient")
test := func(name string, err error, want bool) {
t.Run(name, func(t *testing.T) {
require.Equal(t, want, isTransientReplaceError(err))
})
}
test("access denied", errors.New("Access is denied."), true)
test("sharing violation", errors.New("The process cannot access the file because it is being used by another process."), true)
test("missing file", errors.New("no such file or directory"), false)
test("nil error", nil, false)
}
6 changes: 4 additions & 2 deletions services/nvpair-cluster-manager/invite_expiry_inbound_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -90,8 +91,9 @@ func TestExpireInboundInvite_SignalsInviter(t *testing.T) {
return
}
var env pairingEnvelope
body, _ := io.ReadAll(io.LimitReader(r.Body, maxBodyBytes))
_ = json.Unmarshal(body, &env)
body, err := io.ReadAll(io.LimitReader(r.Body, maxBodyBytes))
assert.NoError(t, err)
assert.NoError(t, json.Unmarshal(body, &env))
respondPairing(w, nil)
select {
case got <- env:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func TestMembersRemove_RevalidatesAfterBlockedBody(t *testing.T) {
victim.addSelfMember()
_, err = pw.Write(payload)
require.NoError(t, err)
_ = pw.Close()
require.NoError(t, pw.Close())

select {
case <-done:
Expand Down
Loading
Loading