Files
2026-07-20 21:03:05 +02:00

92 lines
3.2 KiB
Go

package licenseserver
import (
"bytes"
"crypto/ed25519"
"crypto/rand"
"encoding/json"
"io"
"log/slog"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/b1tsblog/ai-disclosure-standard/pkg/licensekit"
)
func keyPair(t *testing.T) (ed25519.PublicKey, ed25519.PrivateKey) {
t.Helper()
pub, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
return pub, priv
}
func TestRegisterIntrospectAndRevoke(t *testing.T) {
issuerPub, issuerPriv := keyPair(t)
leasePub, leasePriv := keyPair(t)
storeKeys := licensekit.NewTrustStore()
storeKeys.LicenseKeys["issuer"] = licensekit.EncodeKey(issuerPub)
storeKeys.LeaseKeys["lease"] = licensekit.EncodeKey(leasePub)
registry, err := OpenStore("")
if err != nil {
t.Fatal(err)
}
server, err := New(Config{TrustStore: storeKeys, LeasePrivateKey: leasePriv, LeaseKeyID: "lease", AdminToken: "secret", DefaultLeaseTTL: time.Hour}, registry, slog.New(slog.NewTextHandler(io.Discard, nil)))
if err != nil {
t.Fatal(err)
}
httpServer := httptest.NewServer(server.Handler())
defer httpServer.Close()
now := time.Now().UTC()
claims := licensekit.Claims{Version: 1, LicenseID: "lic_1", Issuer: "vendor", Customer: "customer", Product: "product", Edition: "pro", Features: []string{"feature"}, Domains: []string{"*"}, IssuedAt: now.Unix(), ExpiresAt: now.Add(time.Hour).Unix(), Verification: licensekit.VerificationPolicy{Mode: licensekit.ModeHybrid, LeaseTTLSeconds: 300}}
token, err := licensekit.SignLicense(issuerPriv, "issuer", claims)
if err != nil {
t.Fatal(err)
}
registerBody, _ := json.Marshal(map[string]string{"token": token})
req, _ := http.NewRequest(http.MethodPost, httpServer.URL+"/v1/admin/licenses", bytes.NewReader(registerBody))
req.Header.Set("Authorization", "Bearer secret")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
resp.Body.Close()
if resp.StatusCode != http.StatusCreated {
t.Fatalf("register status %d", resp.StatusCode)
}
introBody, _ := json.Marshal(map[string]string{"token": token, "product": "product", "baseUrl": "https://example.org"})
resp, err = http.Post(httpServer.URL+"/v1/introspect", "application/json", bytes.NewReader(introBody))
if err != nil {
t.Fatal(err)
}
var intro map[string]any
_ = json.NewDecoder(resp.Body).Decode(&intro)
resp.Body.Close()
if resp.StatusCode != http.StatusOK || intro["valid"] != true {
t.Fatalf("introspection failed: %d %#v", resp.StatusCode, intro)
}
req, _ = http.NewRequest(http.MethodPost, httpServer.URL+"/v1/admin/licenses/lic_1/revoke", bytes.NewBufferString(`{"reason":"payment"}`))
req.Header.Set("Authorization", "Bearer secret")
req.Header.Set("Content-Type", "application/json")
resp, err = http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatalf("revoke status %d", resp.StatusCode)
}
resp, err = http.Post(httpServer.URL+"/v1/introspect", "application/json", bytes.NewReader(introBody))
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusForbidden {
t.Fatalf("expected revoked status, got %d", resp.StatusCode)
}
}