merge: maestro/tsk_DQ59QeVCOMjq into main (bulk integration)
# Conflicts: # server/cmd/nodectl/main.go
This commit is contained in:
@@ -62,3 +62,8 @@ client/pubspec.lock
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
*.log
|
*.log
|
||||||
*.bak
|
*.bak
|
||||||
|
|
||||||
|
# 编译产物(不应跟踪)
|
||||||
|
/server/server
|
||||||
|
/server/nodectl
|
||||||
|
/server/agent
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/alicebob/miniredis/v2"
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
|
|
||||||
|
"github.com/wangjia/pangolin/server/internal/mtls"
|
||||||
|
)
|
||||||
|
|
||||||
|
// newTestBootstrapManager spins up an in-process miniredis instance and returns
|
||||||
|
// a BootstrapTokenManager backed by it, plus a cleanup function.
|
||||||
|
func newTestBootstrapManager(t *testing.T) (*mtls.BootstrapTokenManager, *miniredis.Miniredis) {
|
||||||
|
t.Helper()
|
||||||
|
mr := miniredis.RunT(t)
|
||||||
|
rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
|
||||||
|
t.Cleanup(func() { rdb.Close() })
|
||||||
|
return mtls.NewBootstrapTokenManager(rdb), mr
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBootstrapTokenCmd_IssuesHexToken verifies that the token manager (the
|
||||||
|
// same one wired into cmdBootstrapToken) produces a well-formed 64-char hex token.
|
||||||
|
func TestBootstrapTokenCmd_IssuesHexToken(t *testing.T) {
|
||||||
|
mgr, _ := newTestBootstrapManager(t)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
const nodeUUID = "11111111-2222-3333-4444-555555555555"
|
||||||
|
token, err := mgr.IssueToken(ctx, nodeUUID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("IssueToken: %v", err)
|
||||||
|
}
|
||||||
|
if len(token) != 64 {
|
||||||
|
t.Errorf("token length = %d; want 64 hex chars", len(token))
|
||||||
|
}
|
||||||
|
// Verify it's pure lowercase hex.
|
||||||
|
const hexChars = "0123456789abcdef"
|
||||||
|
for _, c := range token {
|
||||||
|
if !strings.ContainsRune(hexChars, c) {
|
||||||
|
t.Errorf("token contains non-hex character %q in %q", c, token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBootstrapTokenCmd_ConsumeOnce verifies the one-time guarantee: the token
|
||||||
|
// can be consumed exactly once, and a second call returns an error.
|
||||||
|
func TestBootstrapTokenCmd_ConsumeOnce(t *testing.T) {
|
||||||
|
mgr, _ := newTestBootstrapManager(t)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
const nodeUUID = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
|
||||||
|
token, err := mgr.IssueToken(ctx, nodeUUID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("IssueToken: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// First consume must return the correct UUID.
|
||||||
|
got, err := mgr.ConsumeToken(ctx, token)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ConsumeToken (1st): %v", err)
|
||||||
|
}
|
||||||
|
if got != nodeUUID {
|
||||||
|
t.Errorf("ConsumeToken returned %q; want %q", got, nodeUUID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second consume must fail (one-time guarantee).
|
||||||
|
_, err = mgr.ConsumeToken(ctx, token)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("ConsumeToken (2nd): expected error, got nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBootstrapTokenCmd_MissingNode verifies that cmdBootstrapToken returns an
|
||||||
|
// error when -node is not supplied, without touching Redis.
|
||||||
|
func TestBootstrapTokenCmd_MissingNode(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
err := run(ctx, "bootstrap-token", []string{})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error when -node is empty, got nil")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "-node") {
|
||||||
|
t.Errorf("error message %q should mention -node flag", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBootstrapTokenCmd_UnknownRedis verifies that cmdBootstrapToken returns an
|
||||||
|
// error when REDIS_ADDR points to an unreachable address.
|
||||||
|
func TestBootstrapTokenCmd_UnknownRedis(t *testing.T) {
|
||||||
|
t.Setenv("REDIS_ADDR", "127.0.0.1:1") // port 1 is always unreachable
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
err := run(ctx, "bootstrap-token", []string{"-node", "some-uuid"})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error for unreachable Redis, got nil")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "redis") && !strings.Contains(err.Error(), "connect") {
|
||||||
|
t.Errorf("error %q should mention redis/connect", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestRun_UnknownSubcommand verifies that run returns an error for unrecognised
|
||||||
|
// subcommands (regression guard on the switch statement).
|
||||||
|
func TestRun_UnknownSubcommand(t *testing.T) {
|
||||||
|
err := run(context.Background(), "nonexistent", nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error for unknown subcommand, got nil")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "unknown subcommand") {
|
||||||
|
t.Errorf("error %q should mention 'unknown subcommand'", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Reference in New Issue
Block a user