Files
zeekay f77da86cda Initial luxfi/license — Ed25519-signed offline license tokens for commercial Lux features.
- Stdlib-only deterministic binary token format (no JWT / CBOR dep).
- Go API: VerifyToken, LoadFromEnv, HasFeature, HasFeatureOrDie.
- Stable C ABI + C++17 RAII wrapper for non-Go commercial binaries (luxcpp/cevm).
- cmd/lux-license: keygen / issue / inspect / verify.
- Embedded development key; warns once at runtime until production key swap.
- 22 Go tests + 1 cgo round-trip test, all passing.
2026-05-15 17:08:32 -07:00

98 lines
2.9 KiB
Go

// Copyright (C) 2020-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Package captest builds the C archive of ../../, compiles test_capi.c
// against it, runs the resulting binary with $LUX_LICENSE set to a
// freshly-issued token, and asserts the C test exits 0.
//
// Lives in an internal sub-package because the cgo bridge itself must be
// `package main` (cgo c-archive build mode requirement) and `go test` cannot
// link against a synthesized main.
package captest_test
import (
"os"
"os/exec"
"path/filepath"
"runtime"
"testing"
"time"
"github.com/luxfi/license"
)
func TestCAPIRoundTrip(t *testing.T) {
if _, err := exec.LookPath("cc"); err != nil {
t.Skipf("cc not in PATH: %v", err)
}
if testing.Short() {
t.Skip("skipping cgo build/link in short mode")
}
dir := t.TempDir()
// Locate the cgo bridge package (../../ relative to this test file).
// We resolve via the test's working directory; go test sets it to the
// package directory by default.
wd, err := os.Getwd()
if err != nil {
t.Fatalf("getwd: %v", err)
}
bridgeDir := filepath.Clean(filepath.Join(wd, "..", ".."))
// 1. Build the C archive + header.
archive := filepath.Join(dir, "liblux_license.a")
build := exec.Command("go", "build", "-buildmode=c-archive", "-o", archive, ".")
build.Dir = bridgeDir
build.Env = append(os.Environ(), "GOWORK=off", "CGO_ENABLED=1")
if out, err := build.CombinedOutput(); err != nil {
t.Fatalf("c-archive build failed: %v\n%s", err, out)
}
// 2. Compile the C test against the archive.
bin := filepath.Join(dir, "test_capi")
cSrc := filepath.Join(bridgeDir, "testdata", "test_capi.c")
includeDir := filepath.Join(bridgeDir, "include")
cflags := []string{"-Wall", "-Wextra", "-Werror", "-O2", "-I" + includeDir, "-I" + bridgeDir, cSrc, archive, "-o", bin}
switch runtime.GOOS {
case "darwin":
cflags = append(cflags, "-framework", "CoreFoundation", "-framework", "Security")
case "linux":
cflags = append(cflags, "-lpthread", "-ldl", "-lm")
default:
t.Skipf("unsupported OS for capi test: %s", runtime.GOOS)
}
link := exec.Command("cc", cflags...)
if out, err := link.CombinedOutput(); err != nil {
t.Fatalf("cc link failed: %v\n%s", err, out)
}
// 3. Issue a token signed by the dev key.
priv := license.DevPrivateKey()
c := &license.Claims{
CustomerID: "Acme Inc",
LicenseID: "lic_capi_test_0001",
IssuedAt: time.Now().UTC().Truncate(time.Second),
Expiry: time.Now().UTC().Add(24 * time.Hour),
Scope: []string{"gpu", "dex", "fpga"},
}
tok, err := license.Sign(c, priv)
if err != nil {
t.Fatalf("sign: %v", err)
}
b64 := license.EncodeBase64(tok)
// 4. Run the C test binary.
run := exec.Command(bin)
run.Env = append(os.Environ(),
"LUX_LICENSE="+b64,
"LUX_LICENSE_FILE=",
"LUX_LICENSE_SUPPRESS_DEV_WARNING=1",
)
out, err := run.CombinedOutput()
t.Logf("test_capi output:\n%s", out)
if err != nil {
t.Fatalf("test_capi failed: %v", err)
}
}