package console import ( "context" "crypto/ed25519" "errors" "fmt" "strings" "sync" "testing" "time" "github.com/carlosframework/platform/internal/hostactors" "github.com/carlosframework/platform/internal/hostops" "github.com/carlosframework/platform/internal/release" ) // noMembers is a membership reader that is never asked. type noMembers struct{} func (noMembers) Member(context.Context, string, string) (hostactors.Membership, error) { return hostactors.Membership{}, errors.New("not asked") } func TestEnrollPassIsBuiltFromTheClaimAuthority(t *testing.T) { pub, key, _ := ed25519.GenerateKey(nil) a := &hostops.ProductionAuthority{Directory: hostactors.Directory{Store: release.NewDirStore(t.TempDir()), PublicKey: pub, Bucket: "bucket"}, Signer: release.RootSigner(key), Namespace: "platform"} if _, err := newAutoEnroller(nil, noMembers{}); !errors.Is(err, hostops.ErrContract) { t.Fatalf("no authority: %v", err) } if _, err := newAutoEnroller(a, nil); !errors.Is(err, hostops.ErrContract) { t.Fatalf("no membership reader: %v", err) } e, err := newAutoEnroller(a, noMembers{}) if err != nil { t.Fatal(err) } if e.Namespace != "platform" || e.Pool != "default" || e.Patience != enrollPatience || len(e.Certs) == 0 || e.Directory.Bucket != "bucket" || e.Members != (noMembers{}) || e.Logf == nil { t.Fatalf("enroller %+v", e) } } // A pass that cannot read the roster is logged on its first failure and // every tenth, not every ten seconds, and the loop ends with its context. func TestEnrollPassLogsFailuresSparingly(t *testing.T) { pub, key, _ := ed25519.GenerateKey(nil) e := &hostactors.AutoEnroller{Directory: hostactors.Directory{Store: release.NewDirStore(t.TempDir()), PublicKey: pub, Bucket: "bucket"}, Signer: release.RootSigner(key), Namespace: "platform", Pool: "default", Now: time.Now, Patience: time.Minute} var mu sync.Mutex var lines []string logf := func(format string, args ...any) { mu.Lock() lines = append(lines, fmt.Sprintf(format, args...)) mu.Unlock() } ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() runEnrollPass(ctx, e, 2*time.Millisecond, logf) mu.Lock() defer mu.Unlock() if len(lines) < 2 || !strings.Contains(lines[0], "armed") { t.Fatalf("lines %q", lines) } for i, l := range lines[1:] { var n int if _, err := fmt.Sscanf(l[strings.Index(l, "#"):], "#%d", &n); err != nil || (n != 1 && n%10 != 0) || (i == 0 && n != 1) { t.Fatalf("logged %q", l) } } }