package codes_test import ( "context" "testing" "time" "github.com/wangjia/pangolin/server/internal/codes" ) // TestCreateBatchViaLib verifies CreateBatch mints codes through the shared // codes library (Mint): codes land in the lib's new `codes` table (no // plaintext persisted), the batch's entitlement is a duration payload, and // unknown plans are still rejected up front (old behaviour, service.go:336-339). func TestCreateBatchViaLib(t *testing.T) { ctx := context.Background() db := openMigratedSQLite(t) store := codes.NewStore(db) svc := codes.NewService(store, nil, 5, time.Hour) // rdb=nil: cmd/codegen's real usage res, err := svc.CreateBatch(ctx, codes.BatchRequest{ PlanCode: codes.PlanPro, DurationDays: 30, Count: 5, Channel: codes.ChannelManual, Note: "t", CreatedBy: "admin:1", }) if err != nil { t.Fatalf("CreateBatch: %v", err) } if len(res.Codes) != 5 || res.BatchID == 0 { t.Fatalf("res = %+v", res) } // 码落在库的新表里,明文不落库,权益为 duration payload。 var cnt int if err := db.QueryRow(`SELECT COUNT(*) FROM codes WHERE batch_id=?`, res.BatchID).Scan(&cnt); err != nil || cnt != 5 { t.Fatalf("new codes rows = %d (err=%v), want 5", cnt, err) } var kind, payload string if err := db.QueryRow(`SELECT entitlement_kind, entitlement_payload FROM codes_batches WHERE id=?`, res.BatchID).Scan(&kind, &payload); err != nil { t.Fatalf("batch: %v", err) } if kind != "duration" || payload != `{"plan":"pro","days":30}` { t.Fatalf("kind=%q payload=%s", kind, payload) } for _, plain := range res.Codes { var n int if err := db.QueryRow(`SELECT COUNT(*) FROM codes WHERE code_hash=?`, codes.Hash(plain)).Scan(&n); err != nil || n != 1 { t.Fatalf("hash lookup for %q: n=%d err=%v", plain, n, err) } } // 未知 plan 仍报错(旧行为 service.go:336-339)。 if _, err := svc.CreateBatch(ctx, codes.BatchRequest{ PlanCode: "nope", DurationDays: 30, Count: 1, Channel: codes.ChannelManual, CreatedBy: "x", }); err == nil { t.Fatal("unknown plan should error") } }