tailscale/feature/tailnetlock/tailnetlock_test.go
Alex Chan 8749d19029 tka,ipn: reduce boilerplate in Tailnet Lock tests
The `CreateStateForTest` helper reduces boilerplate in cases where the test
only cares about the trusted keys and not the disablement values (and makes
it more obvious where the disablement values are meaningful).

The `setupChonkStorage` helper reduces the boilerplate when creating on-disk
TKA storage in tests.

The `fakeLocalBackend` helper reduces the boilerplate when setting up a
`LocalBackend` instance in the IPN tests.

Updates #cleanup

Change-Id: Iacfba1be5f7fab208eec11e4369d63c7d7519da5
Signed-off-by: Alex Chan <alexc@tailscale.com>
2026-04-30 12:43:07 +01:00

144 lines
3.6 KiB
Go

// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
package tailnetlock
import (
"bytes"
"encoding/json"
"net/http/httptest"
"strings"
"testing"
"tailscale.com/ipn/ipnlocal"
"tailscale.com/tka"
"tailscale.com/types/key"
"tailscale.com/util/must"
)
func TestHandleC2NDebugTKA(t *testing.T) {
makeTKA := func(length int) (tka.CompactableChonk, *tka.Authority) {
if length == 0 {
return nil, nil
}
signerKey := key.NewNLPrivate()
key1 := tka.Key{Kind: tka.Key25519, Public: signerKey.Public().Verifier(), Votes: 2}
state := tka.CreateStateForTest(key1)
chonk := tka.ChonkMem()
authority, _, err := tka.Create(chonk, state, signerKey)
if err != nil {
t.Fatalf("tka.Create() failed: %v", err)
}
for range length - 1 {
updater := authority.NewUpdater(signerKey)
key2 := tka.Key{Kind: tka.Key25519, Public: key.NewNLPrivate().Public().Verifier(), Votes: 2}
updater.AddKey(key2)
aums := must.Get(updater.Finalize(chonk))
must.Do(authority.Inform(chonk, aums))
}
return chonk, authority
}
bodyHead := func(body *bytes.Buffer) string {
count := 0
var sb strings.Builder
for line := range strings.Lines(body.String()) {
if count == 10 {
sb.WriteString("...")
break
}
sb.WriteString(line)
count++
}
return sb.String()
}
// matches [jsonoutput.PrintNetworkLockLogJSONV1]
type response struct {
SchemaVersion string
Messages []any
}
t.Run("tailnet-lock-disabled", func(t *testing.T) {
b := ipnlocal.LocalBackendWithTKAForTest(nil, nil)
req := httptest.NewRequest("GET", "/debug/tka/log", nil)
rec := httptest.NewRecorder()
b.HandleC2NForTest(rec, req)
if rec.Code != 400 {
t.Fatalf("got status code: %v, want: 400\nBody: %s", rec.Code, rec.Body)
}
})
t.Run("tailnet-lock-enabled", func(t *testing.T) {
chonk, authority := makeTKA(2)
b := ipnlocal.LocalBackendWithTKAForTest(chonk, authority)
req := httptest.NewRequest("GET", "/debug/tka/log", nil)
rec := httptest.NewRecorder()
b.HandleC2NForTest(rec, req)
if rec.Code != 200 {
t.Fatalf("got status code: %v, want: 200\nBody: %s", rec.Code, bodyHead(rec.Body))
}
var got response
if err := json.Unmarshal(rec.Body.Bytes(), &got); err != nil {
t.Fatalf("couldn't parse JSON: %v\nbody: %s", err, bodyHead(rec.Body))
}
if len(got.Messages) != 2 {
t.Fatalf("got %d items, want 2", len(got.Messages))
}
})
t.Run("default-limit", func(t *testing.T) {
chonk, authority := makeTKA(60)
b := ipnlocal.LocalBackendWithTKAForTest(chonk, authority)
req := httptest.NewRequest("GET", "/debug/tka/log", nil)
rec := httptest.NewRecorder()
b.HandleC2NForTest(rec, req)
if rec.Code != 200 {
t.Fatalf("got status code: %v, want: 200\nBody: %s", rec.Code, bodyHead(rec.Body))
}
var got response
if err := json.Unmarshal(rec.Body.Bytes(), &got); err != nil {
t.Fatalf("couldn't parse JSON: %v\nbody: %s", err, bodyHead(rec.Body))
}
if len(got.Messages) != 50 {
t.Fatalf("got %d items, want 50", len(got.Messages))
}
})
t.Run("override-limit", func(t *testing.T) {
chonk, authority := makeTKA(65)
b := ipnlocal.LocalBackendWithTKAForTest(chonk, authority)
req := httptest.NewRequest("GET", "/debug/tka/log?limit=60", nil)
rec := httptest.NewRecorder()
b.HandleC2NForTest(rec, req)
if rec.Code != 200 {
t.Fatalf("got status code: %v, want: 200\nBody: %s", rec.Code, bodyHead(rec.Body))
}
var got response
if err := json.Unmarshal(rec.Body.Bytes(), &got); err != nil {
t.Fatalf("couldn't parse JSON: %v\nbody: %s", err, bodyHead(rec.Body))
}
if len(got.Messages) != 60 {
t.Fatalf("got %d items, want 60", len(got.Messages))
}
})
}