package token_test import ( "testing" "time" "gitea.tecamino.local/paadi/licenseServer/client/hwid" "gitea.tecamino.local/paadi/licenseServer/client/token" ) var secret = []byte("super-secret-key-32-bytes-minimum!") func newPair(t *testing.T) (*token.Generator, *token.Reader) { t.Helper() g, err := token.NewGenerator(secret) if err != nil { t.Fatal(err) } r, err := token.NewReader(secret) if err != nil { t.Fatal(err) } return g, r } // --- Expiry license tests --- func TestExpiryLicense_Valid(t *testing.T) { g, r := newPair(t) tok, err := g.IssueExpiry("myapp", time.Now().Add(24*time.Hour), nil) if err != nil { t.Fatal(err) } fp, err := hwid.Collect() if err != nil { panic(err) } machineID := fp.Hash() result := r.Validate(tok, machineID) if !result.Valid { t.Fatalf("expected valid, got: %v", result.Error) } if result.ExpiresAt == nil { t.Fatal("expected ExpiresAt to be set") } } func TestExpiryLicense_Expired(t *testing.T) { g, r := newPair(t) tok, err := g.IssueExpiry("myapp", time.Now().Add(-1*time.Hour), nil) if err != nil { t.Fatal(err) } fp, err := hwid.Collect() if err != nil { panic(err) } machineID := fp.Hash() result := r.Validate(tok, machineID) if result.Valid { t.Fatal("expected invalid for expired license") } } // --- Usage license tests --- func TestUsageLicense_Valid(t *testing.T) { g, r := newPair(t) tok, err := g.IssueUsage("myapp", 5, nil) if err != nil { t.Fatal(err) } fp, err := hwid.Collect() if err != nil { panic(err) } machineID := fp.Hash() result := r.Validate(tok, machineID) if !result.Valid { t.Fatalf("expected valid: %v", result.Error) } if *result.Remaining != 5 { t.Fatalf("expected 5 remaining, got %d", *result.Remaining) } } func TestUsageLicense_Consume(t *testing.T) { g, r := newPair(t) tok, err := g.IssueUsage("myapp", 3, nil) if err != nil { t.Fatal(err) } fp, err := hwid.Collect() if err != nil { panic(err) } machineID := fp.Hash() for i := int64(2); i >= 0; i-- { res, err := r.Consume(tok, machineID) if err != nil { t.Fatalf("consume failed: %v", err) } if *res.Remaining != i { t.Fatalf("expected %d remaining, got %d", i, *res.Remaining) } } // 4th consume should fail _, err = r.Consume(tok, machineID) if err == nil { t.Fatal("expected error after exhausting usage") } } func TestUsageLicense_Exhausted(t *testing.T) { g, r := newPair(t) tok, err := g.IssueUsage("myapp", 2, nil) if err != nil { t.Fatal(err) } fp, err := hwid.Collect() if err != nil { panic(err) } machineID := fp.Hash() r.Consume(tok, machineID) r.Consume(tok, machineID) result := r.Validate(tok, machineID) if result.Valid { t.Fatal("expected invalid after exhausting uses") } } // --- Tamper / signature tests --- func TestTamperedToken(t *testing.T) { g, r := newPair(t) tok, _ := g.IssueExpiry("myapp", time.Now().Add(24*time.Hour), nil) tampered := token.Token(string(tok) + "x") fp, err := hwid.Collect() if err != nil { panic(err) } machineID := fp.Hash() result := r.Validate(tampered, machineID) if result.Valid { t.Fatal("tampered token should be invalid") } } func TestWrongSecret(t *testing.T) { g, _ := newPair(t) tok, _ := g.IssueExpiry("myapp", time.Now().Add(24*time.Hour), nil) // Reader with different secret r2, _ := token.NewReader([]byte("different-secret-key-32-bytes-xx!")) fp, err := hwid.Collect() if err != nil { panic(err) } machineID := fp.Hash() result := r2.Validate(tok, machineID) if result.Valid { t.Fatal("token signed with different secret should be invalid") } } // --- Usage persistence --- func TestUsageLedger_Persistence(t *testing.T) { g, r := newPair(t) tok, _ := g.IssueUsage("myapp", 10, nil) fp, err := hwid.Collect() if err != nil { panic(err) } machineID := fp.Hash() res, _ := r.Consume(tok, machineID) res, _ = r.Consume(tok, machineID) _ = res snap := r.UsageSnapshot() // Simulate restart: new reader, load snapshot r2, _ := token.NewReader(secret) for id, count := range snap { r2.LoadUsage(id, count) } result := r2.Validate(tok, machineID) if !result.Valid { t.Fatalf("expected valid after reload: %v", result.Error) } if *result.Remaining != 8 { t.Fatalf("expected 8 remaining after reloading 2 uses, got %d", *result.Remaining) } }