2026-05-15 19:26:25 +08:00
package provision
import (
"context"
"database/sql"
"fmt"
"path/filepath"
2026-05-21 21:19:19 +08:00
"strings"
2026-05-15 19:26:25 +08:00
"testing"
_ "modernc.org/sqlite"
"sub2api-cn-relay-manager/internal/host/sub2api"
"sub2api-cn-relay-manager/internal/pack"
"sub2api-cn-relay-manager/internal/store/sqlite"
)
func TestRuntimeImportServicePersistsOperationalState ( t * testing . T ) {
store := openProvisionTestStore ( t )
defer closeProvisionTestStore ( t , store )
2026-05-18 22:22:22 +08:00
seedProvisionHost ( t , store , "host-1" , "https://sub2api.example.com" )
2026-05-15 19:26:25 +08:00
host := & fakeHostAdapter {
batchAccounts : [ ] sub2api . AccountRef { { ID : "account_1" } , { ID : "account_2" } } ,
testResults : map [ string ] sub2api . ProbeResult {
"account_1" : { OK : true , Status : "passed" } ,
"account_2" : { OK : true , Status : "passed" } ,
} ,
models : map [ string ] [ ] sub2api . AccountModel {
"account_1" : { { ID : "deepseek-chat" } } ,
"account_2" : { { ID : "deepseek-chat" } } ,
} ,
gatewayResult : sub2api . GatewayAccessResult { OK : true , StatusCode : 200 , HasExpectedModel : true , Models : [ ] string { "deepseek-chat" } } ,
}
svc := NewRuntimeImportService ( store , host )
result , err := svc . Import ( context . Background ( ) , RuntimeImportRequest {
HostID : "host-1" ,
HostBaseURL : "https://sub2api.example.com" ,
Pack : pack . LoadedPack {
Manifest : pack . Manifest { PackID : "openai-cn-pack" , Version : "1.0.0" , TargetHost : "sub2api" , MinHostVersion : "0.1.126" , MaxHostVersion : "0.2.x" } ,
Checksum : "checksum-1" ,
} ,
Provider : sampleProviderManifest ( ) ,
Mode : ImportModePartial ,
Keys : [ ] string { " key-1 " , "key-2" , "key-1" } ,
Access : AccessRequest {
Mode : AccessModeSelfService ,
ProbeAPIKey : "user-key" ,
} ,
} )
if err != nil {
t . Fatalf ( "RuntimeImportService.Import() error = %v" , err )
}
if result . BatchID <= 0 {
t . Fatalf ( "BatchID = %d, want positive id" , result . BatchID )
}
if result . Report . BatchStatus != BatchStatusSucceeded {
t . Fatalf ( "BatchStatus = %q, want %q" , result . Report . BatchStatus , BatchStatusSucceeded )
}
if got := queryCount ( t , store . SQLDB ( ) , "hosts" ) ; got != 1 {
t . Fatalf ( "hosts row count = %d, want 1" , got )
}
if got := queryCount ( t , store . SQLDB ( ) , "packs" ) ; got != 1 {
t . Fatalf ( "packs row count = %d, want 1" , got )
}
if got := queryCount ( t , store . SQLDB ( ) , "providers" ) ; got != 1 {
t . Fatalf ( "providers row count = %d, want 1" , got )
}
if got := queryCount ( t , store . SQLDB ( ) , "import_batches" ) ; got != 1 {
t . Fatalf ( "import_batches row count = %d, want 1" , got )
}
if got := queryCount ( t , store . SQLDB ( ) , "import_batch_items" ) ; got != 2 {
t . Fatalf ( "import_batch_items row count = %d, want 2" , got )
}
if got := queryCount ( t , store . SQLDB ( ) , "managed_resources" ) ; got != 4 {
t . Fatalf ( "managed_resources row count = %d, want 4" , got )
}
if got := queryCount ( t , store . SQLDB ( ) , "probe_results" ) ; got != 2 {
t . Fatalf ( "probe_results row count = %d, want 2" , got )
}
if got := queryCount ( t , store . SQLDB ( ) , "access_closure_records" ) ; got != 1 {
t . Fatalf ( "access_closure_records row count = %d, want 1" , got )
}
var batchStatus string
var accessStatus string
if err := store . SQLDB ( ) . QueryRowContext ( context . Background ( ) , "SELECT batch_status, access_status FROM import_batches WHERE id = ?" , result . BatchID ) . Scan ( & batchStatus , & accessStatus ) ; err != nil {
t . Fatalf ( "query import batch state: %v" , err )
}
if batchStatus != BatchStatusSucceeded {
t . Fatalf ( "persisted batch_status = %q, want %q" , batchStatus , BatchStatusSucceeded )
}
if accessStatus != AccessStatusSelfServiceReady {
t . Fatalf ( "persisted access_status = %q, want %q" , accessStatus , AccessStatusSelfServiceReady )
}
var fingerprint string
var accountStatus string
if err := store . SQLDB ( ) . QueryRowContext ( context . Background ( ) , "SELECT key_fingerprint, account_status FROM import_batch_items ORDER BY id LIMIT 1" ) . Scan ( & fingerprint , & accountStatus ) ; err != nil {
t . Fatalf ( "query import batch item: %v" , err )
}
if fingerprint == "key-1" || fingerprint == "key-2" || len ( fingerprint ) < 10 {
t . Fatalf ( "key_fingerprint = %q, want hashed fingerprint instead of raw key" , fingerprint )
}
if accountStatus != "passed" {
t . Fatalf ( "account_status = %q, want passed" , accountStatus )
}
}
func TestRuntimeImportServicePersistsFailedBatchAfterStrictRollback ( t * testing . T ) {
store := openProvisionTestStore ( t )
defer closeProvisionTestStore ( t , store )
2026-05-18 22:22:22 +08:00
seedProvisionHost ( t , store , "host-1" , "https://sub2api.example.com" )
2026-05-15 19:26:25 +08:00
host := & fakeHostAdapter {
batchAccounts : [ ] sub2api . AccountRef { { ID : "account_1" } , { ID : "account_2" } } ,
testResults : map [ string ] sub2api . ProbeResult {
"account_1" : { OK : true , Status : "passed" } ,
"account_2" : { OK : false , Status : "failed" , Message : "bad key" } ,
} ,
models : map [ string ] [ ] sub2api . AccountModel {
"account_1" : { { ID : "deepseek-chat" } } ,
"account_2" : { { ID : "deepseek-chat" } } ,
} ,
}
svc := NewRuntimeImportService ( store , host )
result , err := svc . Import ( context . Background ( ) , RuntimeImportRequest {
HostID : "host-1" ,
HostBaseURL : "https://sub2api.example.com" ,
Pack : pack . LoadedPack {
Manifest : pack . Manifest { PackID : "openai-cn-pack" , Version : "1.0.0" , TargetHost : "sub2api" , MinHostVersion : "0.1.126" , MaxHostVersion : "0.2.x" } ,
Checksum : "checksum-1" ,
} ,
Provider : sampleProviderManifest ( ) ,
Mode : ImportModeStrict ,
Keys : [ ] string { "key-1" , "key-2" } ,
Access : AccessRequest {
Mode : AccessModeSelfService ,
ProbeAPIKey : "user-key" ,
} ,
} )
if err == nil {
t . Fatal ( "RuntimeImportService.Import() error = nil, want strict failure" )
}
if result . BatchID <= 0 {
t . Fatalf ( "BatchID = %d, want positive id" , result . BatchID )
}
var batchStatus string
var accessStatus string
if err := store . SQLDB ( ) . QueryRowContext ( context . Background ( ) , "SELECT batch_status, access_status FROM import_batches WHERE id = ?" , result . BatchID ) . Scan ( & batchStatus , & accessStatus ) ; err != nil {
t . Fatalf ( "query failed import batch state: %v" , err )
}
if batchStatus != BatchStatusFailed {
t . Fatalf ( "persisted batch_status = %q, want %q" , batchStatus , BatchStatusFailed )
}
if accessStatus != AccessStatusBroken {
t . Fatalf ( "persisted access_status = %q, want %q" , accessStatus , AccessStatusBroken )
}
if got := queryCount ( t , store . SQLDB ( ) , "managed_resources" ) ; got != 0 {
t . Fatalf ( "managed_resources row count = %d, want 0 after strict rollback" , got )
}
if got := queryCount ( t , store . SQLDB ( ) , "probe_results" ) ; got != 2 {
t . Fatalf ( "probe_results row count = %d, want 2" , got )
}
if got := queryCount ( t , store . SQLDB ( ) , "access_closure_records" ) ; got != 1 {
t . Fatalf ( "access_closure_records row count = %d, want 1" , got )
}
}
2026-05-21 21:19:19 +08:00
func TestRuntimeImportServicePersistsWarningAccountStatusForAdvisoryProbeFailure ( t * testing . T ) {
store := openProvisionTestStore ( t )
defer closeProvisionTestStore ( t , store )
seedProvisionHost ( t , store , "host-1" , "https://sub2api.example.com" )
host := & fakeHostAdapter {
batchAccounts : [ ] sub2api . AccountRef { { ID : "account_1" } } ,
testResults : map [ string ] sub2api . ProbeResult {
"account_1" : {
OK : false ,
Status : "failed" ,
Message : "账号本身可正常使用,但当前测试接口仅支持 Responses API 路径。请直接通过实际 API 调用验证。" ,
} ,
} ,
models : map [ string ] [ ] sub2api . AccountModel {
"account_1" : { { ID : "deepseek-chat" } } ,
2026-05-22 12:33:12 +08:00
} ,
gatewayResult : sub2api . GatewayAccessResult {
OK : true ,
StatusCode : 200 ,
HasExpectedModel : true ,
Models : [ ] string { "deepseek-chat" } ,
CompletionOK : true ,
CompletionStatus : 200 ,
} ,
}
svc := NewRuntimeImportService ( store , host )
result , err := svc . Import ( context . Background ( ) , RuntimeImportRequest {
HostID : "host-1" ,
HostBaseURL : "https://sub2api.example.com" ,
Pack : pack . LoadedPack {
Manifest : pack . Manifest { PackID : "openai-cn-pack" , Version : "1.0.0" , TargetHost : "sub2api" , MinHostVersion : "0.1.126" , MaxHostVersion : "0.2.x" } ,
Checksum : "checksum-1" ,
} ,
Provider : sampleProviderManifest ( ) ,
Mode : ImportModePartial ,
Keys : [ ] string { "key-1" } ,
Access : AccessRequest {
Mode : AccessModeSelfService ,
ProbeAPIKey : "user-key" ,
} ,
} )
if err != nil {
t . Fatalf ( "RuntimeImportService.Import() error = %v" , err )
}
if result . Report . BatchStatus != BatchStatusSucceeded {
t . Fatalf ( "BatchStatus = %q, want %q" , result . Report . BatchStatus , BatchStatusSucceeded )
}
var accountStatus string
var summary string
if err := store . SQLDB ( ) . QueryRowContext ( context . Background ( ) , "SELECT account_status, probe_summary_json FROM import_batch_items WHERE batch_id = ? ORDER BY id LIMIT 1" , result . BatchID ) . Scan ( & accountStatus , & summary ) ; err != nil {
t . Fatalf ( "query import batch item: %v" , err )
}
if accountStatus != AccountStatusWarning {
t . Fatalf ( "account_status = %q, want %q" , accountStatus , AccountStatusWarning )
}
if ! strings . Contains ( summary , "\"probe_advisory\":true" ) {
t . Fatalf ( "probe_summary_json = %s, want probe_advisory=true" , summary )
}
}
func TestRuntimeImportServicePersistsWarningAccountStatusForForbiddenProbeRace ( t * testing . T ) {
store := openProvisionTestStore ( t )
defer closeProvisionTestStore ( t , store )
seedProvisionHost ( t , store , "host-1" , "https://sub2api.example.com" )
host := & fakeHostAdapter {
batchAccounts : [ ] sub2api . AccountRef { { ID : "account_1" } } ,
testResults : map [ string ] sub2api . ProbeResult {
"account_1" : {
OK : false ,
Status : "failed" ,
Message : "API returned 403: Forbidden" ,
} ,
} ,
models : map [ string ] [ ] sub2api . AccountModel {
"account_1" : { { ID : "deepseek-chat" } } ,
2026-05-21 21:19:19 +08:00
} ,
gatewayResult : sub2api . GatewayAccessResult {
OK : true ,
StatusCode : 200 ,
HasExpectedModel : true ,
Models : [ ] string { "deepseek-chat" } ,
CompletionOK : true ,
CompletionStatus : 200 ,
} ,
}
svc := NewRuntimeImportService ( store , host )
result , err := svc . Import ( context . Background ( ) , RuntimeImportRequest {
HostID : "host-1" ,
HostBaseURL : "https://sub2api.example.com" ,
Pack : pack . LoadedPack {
Manifest : pack . Manifest { PackID : "openai-cn-pack" , Version : "1.0.0" , TargetHost : "sub2api" , MinHostVersion : "0.1.126" , MaxHostVersion : "0.2.x" } ,
Checksum : "checksum-1" ,
} ,
Provider : sampleProviderManifest ( ) ,
Mode : ImportModePartial ,
Keys : [ ] string { "key-1" } ,
Access : AccessRequest {
Mode : AccessModeSelfService ,
ProbeAPIKey : "user-key" ,
} ,
} )
if err != nil {
t . Fatalf ( "RuntimeImportService.Import() error = %v" , err )
}
if result . Report . BatchStatus != BatchStatusSucceeded {
t . Fatalf ( "BatchStatus = %q, want %q" , result . Report . BatchStatus , BatchStatusSucceeded )
}
var accountStatus string
var summary string
if err := store . SQLDB ( ) . QueryRowContext ( context . Background ( ) , "SELECT account_status, probe_summary_json FROM import_batch_items WHERE batch_id = ? ORDER BY id LIMIT 1" , result . BatchID ) . Scan ( & accountStatus , & summary ) ; err != nil {
t . Fatalf ( "query import batch item: %v" , err )
}
if accountStatus != AccountStatusWarning {
t . Fatalf ( "account_status = %q, want %q" , accountStatus , AccountStatusWarning )
}
if ! strings . Contains ( summary , "\"probe_advisory\":true" ) {
t . Fatalf ( "probe_summary_json = %s, want probe_advisory=true" , summary )
}
}
2026-05-18 22:22:22 +08:00
func TestRuntimeImportServicePersistsPartialManagedResourcesOnAccessFailure ( t * testing . T ) {
store := openProvisionTestStore ( t )
defer closeProvisionTestStore ( t , store )
seedProvisionHost ( t , store , "host-1" , "https://sub2api.example.com" )
host := & fakeHostAdapter {
batchAccounts : [ ] sub2api . AccountRef { { ID : "account_1" } } ,
testResults : map [ string ] sub2api . ProbeResult {
"account_1" : { OK : true , Status : "passed" } ,
} ,
models : map [ string ] [ ] sub2api . AccountModel {
"account_1" : { { ID : "deepseek-chat" } } ,
} ,
assignErr : fmt . Errorf ( "group is not a subscription type" ) ,
}
svc := NewRuntimeImportService ( store , host )
result , err := svc . Import ( context . Background ( ) , RuntimeImportRequest {
HostID : "host-1" ,
HostBaseURL : "https://sub2api.example.com" ,
Pack : pack . LoadedPack {
Manifest : pack . Manifest { PackID : "openai-cn-pack" , Version : "1.0.0" , TargetHost : "sub2api" , MinHostVersion : "0.1.126" , MaxHostVersion : "0.2.x" } ,
Checksum : "checksum-1" ,
} ,
Provider : sampleProviderManifest ( ) ,
Mode : ImportModePartial ,
Keys : [ ] string { "key-1" } ,
Access : AccessRequest {
Mode : AccessModeSubscription ,
ProbeAPIKey : "user-key" ,
Subscriptions : [ ] SubscriptionTarget { { UserID : "1" , DurationDays : 30 } } ,
} ,
} )
if err == nil {
t . Fatal ( "RuntimeImportService.Import() error = nil, want partial failure" )
}
if result . BatchID <= 0 {
t . Fatalf ( "BatchID = %d, want positive id" , result . BatchID )
}
if got := queryCount ( t , store . SQLDB ( ) , "managed_resources" ) ; got != 4 {
t . Fatalf ( "managed_resources row count = %d, want 4 persisted resources on partial failure" , got )
}
var batchStatus string
if err := store . SQLDB ( ) . QueryRowContext ( context . Background ( ) , "SELECT batch_status FROM import_batches WHERE id = ?" , result . BatchID ) . Scan ( & batchStatus ) ; err != nil {
t . Fatalf ( "query import batch status: %v" , err )
}
if batchStatus != BatchStatusPartial {
t . Fatalf ( "persisted batch_status = %q, want %q" , batchStatus , BatchStatusPartial )
}
}
func TestRuntimeImportServiceRepeatedImportReusesManagedResources ( t * testing . T ) {
store := openProvisionTestStore ( t )
defer closeProvisionTestStore ( t , store )
seedProvisionHost ( t , store , "host-1" , "https://sub2api.example.com" )
host := & fakeHostAdapter {
batchAccounts : [ ] sub2api . AccountRef { { ID : "account_1" , Name : "key-1" } } ,
testResults : map [ string ] sub2api . ProbeResult {
"account_1" : { OK : true , Status : "passed" } ,
} ,
models : map [ string ] [ ] sub2api . AccountModel {
"account_1" : { { ID : "deepseek-chat" } } ,
} ,
gatewayResult : sub2api . GatewayAccessResult { OK : true , StatusCode : 200 , HasExpectedModel : true , Models : [ ] string { "deepseek-chat" } } ,
}
svc := NewRuntimeImportService ( store , host )
request := RuntimeImportRequest {
HostID : "host-1" ,
HostBaseURL : "https://sub2api.example.com" ,
Pack : pack . LoadedPack {
Manifest : pack . Manifest { PackID : "openai-cn-pack" , Version : "1.0.0" , TargetHost : "sub2api" , MinHostVersion : "0.1.126" , MaxHostVersion : "0.2.x" } ,
Checksum : "checksum-1" ,
} ,
Provider : sampleProviderManifest ( ) ,
Mode : ImportModePartial ,
Keys : [ ] string { "key-1" } ,
Access : AccessRequest {
Mode : AccessModeSelfService ,
ProbeAPIKey : "user-key" ,
} ,
}
first , err := svc . Import ( context . Background ( ) , request )
if err != nil {
t . Fatalf ( "first Import() error = %v" , err )
}
second , err := svc . Import ( context . Background ( ) , request )
if err != nil {
t . Fatalf ( "second Import() error = %v" , err )
}
if second . BatchID <= first . BatchID {
t . Fatalf ( "second BatchID = %d, want > first BatchID %d" , second . BatchID , first . BatchID )
}
if got := queryCount ( t , store . SQLDB ( ) , "managed_resources" ) ; got != 3 {
t . Fatalf ( "managed_resources row count = %d, want 3 after reused import" , got )
}
if got := queryCount ( t , store . SQLDB ( ) , "import_batches" ) ; got != 2 {
t . Fatalf ( "import_batches row count = %d, want 2" , got )
}
}
2026-05-21 09:18:17 +08:00
func TestRuntimeImportServiceImportReconcilesExistingChannelConfiguration ( t * testing . T ) {
store := openProvisionTestStore ( t )
defer closeProvisionTestStore ( t , store )
seedProvisionHost ( t , store , "host-1" , "https://sub2api.example.com" )
host := & fakeHostAdapter {
batchAccounts : [ ] sub2api . AccountRef { { ID : "account_1" , Name : "minimax-01" } } ,
testResults : map [ string ] sub2api . ProbeResult {
"account_1" : { OK : true , Status : "passed" } ,
} ,
models : map [ string ] [ ] sub2api . AccountModel {
"account_1" : { { ID : "MiniMax-M2.7-highspeed" } } ,
} ,
gatewayResult : sub2api . GatewayAccessResult { OK : true , StatusCode : 200 , HasExpectedModel : true , Models : [ ] string { "MiniMax-M2.5-highspeed" , "MiniMax-M2.7-highspeed" } } ,
managedSnapshot : sub2api . ManagedResourceSnapshot {
Groups : [ ] sub2api . NamedResource { { ID : "group_existing" , Name : "MiniMax 默认分组-self-service" } } ,
Channels : [ ] sub2api . NamedResource { { ID : "channel_existing" , Name : "MiniMax 默认渠道-self-service" } } ,
Plans : [ ] sub2api . NamedResource { { ID : "plan_existing" , Name : "MiniMax 默认套餐-self-service" } } ,
} ,
}
provider := sampleProviderManifest ( )
provider . ProviderID = "minimax"
provider . DisplayName = "MiniMax OpenAI Compatible"
provider . BaseURL = "https://v2.aicodee.com/v1"
provider . DefaultModels = [ ] string { "MiniMax-M2.5-highspeed" , "MiniMax-M2.7-highspeed" }
provider . SmokeTestModel = "MiniMax-M2.7-highspeed"
provider . GroupTemplate . Name = "MiniMax 默认分组"
provider . ChannelTemplate = pack . ChannelTemplate {
Name : "MiniMax 默认渠道" ,
ModelMapping : map [ string ] string { "MiniMax-M2.5-highspeed" : "MiniMax-M2.5-highspeed" , "MiniMax-M2.7-highspeed" : "MiniMax-M2.7-highspeed" } ,
}
provider . PlanTemplate . Name = "MiniMax 默认套餐"
svc := NewRuntimeImportService ( store , host )
result , err := svc . Import ( context . Background ( ) , RuntimeImportRequest {
HostID : "host-1" ,
HostBaseURL : "https://sub2api.example.com" ,
Pack : pack . LoadedPack {
Manifest : pack . Manifest { PackID : "openai-cn-pack" , Version : "1.0.0" , TargetHost : "sub2api" , MinHostVersion : "0.1.126" , MaxHostVersion : "0.2.x" } ,
Checksum : "checksum-1" ,
} ,
Provider : provider ,
Mode : ImportModePartial ,
Keys : [ ] string { "key-1" } ,
Access : AccessRequest {
Mode : AccessModeSelfService ,
ProbeAPIKey : "user-key" ,
} ,
} )
if err != nil {
t . Fatalf ( "RuntimeImportService.Import() error = %v" , err )
}
if result . Report . Channel . ID != "channel_existing" {
t . Fatalf ( "Channel.ID = %q, want reused channel_existing" , result . Report . Channel . ID )
}
if host . createChannelCalls != 0 {
t . Fatalf ( "CreateChannel() calls = %d, want 0 when channel already exists" , host . createChannelCalls )
}
if host . updateChannelCalls != 1 {
t . Fatalf ( "UpdateChannel() calls = %d, want 1" , host . updateChannelCalls )
}
if host . updateChannelID != "channel_existing" {
t . Fatalf ( "UpdateChannel() id = %q, want channel_existing" , host . updateChannelID )
}
if len ( host . updateChannelReq . ModelPricing ) != 1 {
t . Fatalf ( "UpdateChannel().ModelPricing len = %d, want 1" , len ( host . updateChannelReq . ModelPricing ) )
}
if got := host . updateChannelReq . ModelPricing [ 0 ] . Models ; len ( got ) != 2 || got [ 0 ] != "MiniMax-M2.5-highspeed" || got [ 1 ] != "MiniMax-M2.7-highspeed" {
t . Fatalf ( "UpdateChannel().ModelPricing[0].Models = %v, want minimax default models" , got )
}
if host . updateChannelReq . ModelPricing [ 0 ] . BillingMode != "token" {
t . Fatalf ( "UpdateChannel().ModelPricing[0].BillingMode = %q, want token" , host . updateChannelReq . ModelPricing [ 0 ] . BillingMode )
}
}
2026-05-15 19:26:25 +08:00
func openProvisionTestStore ( t * testing . T ) * sqlite . DB {
t . Helper ( )
dbPath := filepath . Join ( t . TempDir ( ) , "state.db" )
dsn := fmt . Sprintf ( "file:%s?_busy_timeout=5000&_pragma=foreign_keys(0)" , filepath . ToSlash ( dbPath ) )
store , err := sqlite . Open ( context . Background ( ) , dsn )
if err != nil {
t . Fatalf ( "sqlite.Open() error = %v" , err )
}
return store
}
func closeProvisionTestStore ( t * testing . T , store * sqlite . DB ) {
t . Helper ( )
if err := store . Close ( ) ; err != nil {
t . Fatalf ( "store.Close() error = %v" , err )
}
}
2026-05-18 22:22:22 +08:00
func seedProvisionHost ( t * testing . T , store * sqlite . DB , hostID , baseURL string ) int64 {
t . Helper ( )
id , err := store . Hosts ( ) . Create ( context . Background ( ) , sqlite . Host {
HostID : hostID ,
BaseURL : baseURL ,
HostVersion : "0.1.126" ,
AuthType : "apikey" ,
AuthToken : "test-host-token" ,
} )
if err != nil {
t . Fatalf ( "Hosts().Create() error = %v" , err )
}
return id
}
2026-05-15 19:26:25 +08:00
func queryCount ( t * testing . T , db * sql . DB , table string ) int {
t . Helper ( )
var count int
if err := db . QueryRowContext ( context . Background ( ) , "SELECT COUNT(*) FROM " + table ) . Scan ( & count ) ; err != nil {
t . Fatalf ( "count rows for %s: %v" , table , err )
}
return count
}