refactor(backend): migrate to modular repository pattern with separated concerns

- Extract database layer into model and repo packages
- Split repository into focused modules (control, federation, flow, groups, mutations)
- Remove monolithic db.go and sqlite/repository.go
- Update handlers to use new repository structure
- Migrate contract tests to new patterns
- Add migration plan documentation
This commit is contained in:
Antigravity
2026-02-17 04:47:11 +00:00
parent 98b4d78b4d
commit 66be07750f
44 changed files with 6829 additions and 6188 deletions
@@ -16,43 +16,41 @@ import (
httpserver "go-backend/internal/http"
"go-backend/internal/http/handler"
"go-backend/internal/http/response"
"go-backend/internal/store/sqlite"
"go-backend/internal/store/repo"
)
func TestDiagnosisChainCoverageContracts(t *testing.T) {
secret := "contract-jwt-secret"
router, repo := setupDiagnosisContractRouter(t, secret)
router, r := setupDiagnosisContractRouter(t, secret)
now := time.Now().UnixMilli()
if _, err := repo.DB().Exec(`
if err := r.DB().Exec(`
INSERT INTO user(id, user, pwd, role_id, exp_time, flow, in_flow, out_flow, flow_reset_time, num, created_time, updated_time, status)
VALUES(2, 'normal_user', '3c85cdebade1c51cf64ca9f3c09d182d', 1, 2727251700000, 99999, 0, 0, 1, 99999, ?, ?, 1)
`, now, now); err != nil {
`, now, now).Error; err != nil {
t.Fatalf("insert user: %v", err)
}
tunnelRes, err := repo.DB().Exec(`
if err := r.DB().Exec(`
INSERT INTO tunnel(name, traffic_ratio, type, protocol, flow, created_time, updated_time, status, in_ip, inx)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, "diagnose-chain-tunnel", 1.0, 2, "tls", 99999, now, now, 1, nil, 0)
if err != nil {
`, "diagnose-chain-tunnel", 1.0, 2, "tls", 99999, now, now, 1, nil, 0).Error; err != nil {
t.Fatalf("insert tunnel: %v", err)
}
tunnelID, err := tunnelRes.LastInsertId()
if err != nil {
var tunnelID int64
if err := r.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&tunnelID); err != nil {
t.Fatalf("get tunnel id: %v", err)
}
insertNode := func(name, ip string) int64 {
res, err := repo.DB().Exec(`
if err := r.DB().Exec(`
INSERT INTO node(name, secret, server_ip, server_ip_v4, server_ip_v6, port, interface_name, version, http, tls, socks, created_time, updated_time, status, tcp_listen_addr, udp_listen_addr, inx)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, name, name+"-secret", ip, ip, "", "30000-30010", "", "v1", 1, 1, 1, now, now, 1, "[::]", "[::]", 0)
if err != nil {
`, name, name+"-secret", ip, ip, "", "30000-30010", "", "v1", 1, 1, 1, now, now, 1, "[::]", "[::]", 0).Error; err != nil {
t.Fatalf("insert node %s: %v", name, err)
}
id, err := res.LastInsertId()
if err != nil {
var id int64
if err := r.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&id); err != nil {
t.Fatalf("get node id %s: %v", name, err)
}
return id
@@ -62,34 +60,33 @@ func TestDiagnosisChainCoverageContracts(t *testing.T) {
chainNodeID := insertNode("chain-node", "10.0.1.20")
exitNodeID := insertNode("exit-node", "10.0.1.30")
if _, err := repo.DB().Exec(`
if err := r.DB().Exec(`
INSERT INTO chain_tunnel(tunnel_id, chain_type, node_id, port, strategy, inx, protocol)
VALUES(?, 1, ?, 30001, 'round', 1, 'tls')
`, tunnelID, entryNodeID); err != nil {
`, tunnelID, entryNodeID).Error; err != nil {
t.Fatalf("insert entry chain: %v", err)
}
if _, err := repo.DB().Exec(`
if err := r.DB().Exec(`
INSERT INTO chain_tunnel(tunnel_id, chain_type, node_id, port, strategy, inx, protocol)
VALUES(?, 2, ?, 30002, 'round', 1, 'tls')
`, tunnelID, chainNodeID); err != nil {
`, tunnelID, chainNodeID).Error; err != nil {
t.Fatalf("insert middle chain: %v", err)
}
if _, err := repo.DB().Exec(`
if err := r.DB().Exec(`
INSERT INTO chain_tunnel(tunnel_id, chain_type, node_id, port, strategy, inx, protocol)
VALUES(?, 3, ?, 30003, 'round', 1, 'tls')
`, tunnelID, exitNodeID); err != nil {
`, tunnelID, exitNodeID).Error; err != nil {
t.Fatalf("insert exit chain: %v", err)
}
forwardRes, err := repo.DB().Exec(`
if err := r.DB().Exec(`
INSERT INTO forward(user_id, user_name, name, tunnel_id, remote_addr, strategy, in_flow, out_flow, created_time, updated_time, status, inx)
VALUES(?, ?, ?, ?, ?, ?, 0, 0, ?, ?, 1, ?)
`, 2, "normal_user", "chain-forward", tunnelID, "8.8.8.8:53", "fifo", now, now, 0)
if err != nil {
`, 2, "normal_user", "chain-forward", tunnelID, "8.8.8.8:53", "fifo", now, now, 0).Error; err != nil {
t.Fatalf("insert forward: %v", err)
}
forwardID, err := forwardRes.LastInsertId()
if err != nil {
var forwardID int64
if err := r.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&forwardID); err != nil {
t.Fatalf("get forward id: %v", err)
}
@@ -208,7 +205,7 @@ func TestDiagnosisChainCoverageContracts(t *testing.T) {
func TestDiagnosisUsesFederationRuntimeForRemoteNodes(t *testing.T) {
secret := "contract-jwt-secret"
router, repo := setupDiagnosisContractRouter(t, secret)
router, r := setupDiagnosisContractRouter(t, secret)
now := time.Now().UnixMilli()
remoteToken := "remote-diagnose-token"
@@ -256,30 +253,28 @@ func TestDiagnosisUsesFederationRuntimeForRemoteNodes(t *testing.T) {
defer remoteServer.Close()
insertLocalNode := func(name, ip string) int64 {
res, err := repo.DB().Exec(`
if err := r.DB().Exec(`
INSERT INTO node(name, secret, server_ip, server_ip_v4, server_ip_v6, port, interface_name, version, http, tls, socks, created_time, updated_time, status, tcp_listen_addr, udp_listen_addr, inx)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, name, name+"-secret", ip, ip, "", "30000-30010", "", "v1", 1, 1, 1, now, now, 1, "[::]", "[::]", 0)
if err != nil {
`, name, name+"-secret", ip, ip, "", "30000-30010", "", "v1", 1, 1, 1, now, now, 1, "[::]", "[::]", 0).Error; err != nil {
t.Fatalf("insert local node %s: %v", name, err)
}
id, err := res.LastInsertId()
if err != nil {
var id int64
if err := r.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&id); err != nil {
t.Fatalf("get local node id %s: %v", name, err)
}
return id
}
insertRemoteNode := func(name, ip string) int64 {
res, err := repo.DB().Exec(`
if err := r.DB().Exec(`
INSERT INTO node(name, secret, server_ip, server_ip_v4, server_ip_v6, port, interface_name, version, http, tls, socks, created_time, updated_time, status, tcp_listen_addr, udp_listen_addr, inx, is_remote, remote_url, remote_token, remote_config)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, 0, 0, 0, ?, ?, 1, ?, ?, ?, 1, ?, ?, ?)
`, name, name+"-secret", ip, "", "", "31000-31010", "", "", now, now, "[::]", "[::]", 1, remoteServer.URL, remoteToken, `{"shareId": 123}`)
if err != nil {
`, name, name+"-secret", ip, "", "", "31000-31010", "", "", now, now, "[::]", "[::]", 1, remoteServer.URL, remoteToken, `{"shareId": 123}`).Error; err != nil {
t.Fatalf("insert remote node %s: %v", name, err)
}
id, err := res.LastInsertId()
if err != nil {
var id int64
if err := r.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&id); err != nil {
t.Fatalf("get remote node id %s: %v", name, err)
}
return id
@@ -289,34 +284,33 @@ func TestDiagnosisUsesFederationRuntimeForRemoteNodes(t *testing.T) {
remoteChainNodeID := insertRemoteNode("middle-remote", "10.50.0.20")
exitNodeID := insertLocalNode("exit-local", "10.50.0.30")
tunnelRes, err := repo.DB().Exec(`
if err := r.DB().Exec(`
INSERT INTO tunnel(name, traffic_ratio, type, protocol, flow, created_time, updated_time, status, in_ip, inx)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, "diagnose-remote-tunnel", 1.0, 2, "tls", 99999, now, now, 1, nil, 0)
if err != nil {
`, "diagnose-remote-tunnel", 1.0, 2, "tls", 99999, now, now, 1, nil, 0).Error; err != nil {
t.Fatalf("insert tunnel: %v", err)
}
tunnelID, err := tunnelRes.LastInsertId()
if err != nil {
var tunnelID int64
if err := r.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&tunnelID); err != nil {
t.Fatalf("get tunnel id: %v", err)
}
if _, err := repo.DB().Exec(`
if err := r.DB().Exec(`
INSERT INTO chain_tunnel(tunnel_id, chain_type, node_id, port, strategy, inx, protocol)
VALUES(?, 1, ?, 30001, 'round', 1, 'tls')
`, tunnelID, entryNodeID); err != nil {
`, tunnelID, entryNodeID).Error; err != nil {
t.Fatalf("insert entry chain: %v", err)
}
if _, err := repo.DB().Exec(`
if err := r.DB().Exec(`
INSERT INTO chain_tunnel(tunnel_id, chain_type, node_id, port, strategy, inx, protocol)
VALUES(?, 2, ?, 30002, 'round', 1, 'tls')
`, tunnelID, remoteChainNodeID); err != nil {
`, tunnelID, remoteChainNodeID).Error; err != nil {
t.Fatalf("insert middle chain: %v", err)
}
if _, err := repo.DB().Exec(`
if err := r.DB().Exec(`
INSERT INTO chain_tunnel(tunnel_id, chain_type, node_id, port, strategy, inx, protocol)
VALUES(?, 3, ?, 30003, 'round', 1, 'tls')
`, tunnelID, exitNodeID); err != nil {
`, tunnelID, exitNodeID).Error; err != nil {
t.Fatalf("insert exit chain: %v", err)
}
@@ -409,17 +403,17 @@ func valueAsBool(v interface{}) bool {
}
}
func setupDiagnosisContractRouter(t *testing.T, jwtSecret string) (http.Handler, *sqlite.Repository) {
func setupDiagnosisContractRouter(t *testing.T, jwtSecret string) (http.Handler, *repo.Repository) {
t.Helper()
dbPath := filepath.Join(t.TempDir(), "diagnosis-contract.db")
repo, err := sqlite.Open(dbPath)
r, err := repo.Open(dbPath)
if err != nil {
t.Fatalf("open sqlite: %v", err)
}
t.Cleanup(func() {
_ = repo.Close()
_ = r.Close()
})
h := handler.New(repo, jwtSecret)
return httpserver.NewRouter(h, jwtSecret), repo
h := handler.New(r, jwtSecret)
return httpserver.NewRouter(h, jwtSecret), r
}
@@ -17,7 +17,7 @@ import (
"go-backend/internal/auth"
"go-backend/internal/http/response"
"go-backend/internal/security"
"go-backend/internal/store/sqlite"
"go-backend/internal/store/repo"
)
func TestFederationDualPanelMiddleExitAutoPortContract(t *testing.T) {
@@ -39,7 +39,7 @@ func TestFederationDualPanelMiddleExitAutoPortContract(t *testing.T) {
providerMiddleNodeID := insertContractNode(t, providerRepo, "provider-middle", "198.51.100.12", "44000-44010", "provider-middle-secret", 1)
providerExitNodeID := insertContractNode(t, providerRepo, "provider-exit", "198.51.100.13", "45000-45010", "provider-exit-secret", 1)
entryShareID := insertPeerShare(t, providerRepo, &sqlite.PeerShare{
entryShareID := insertPeerShare(t, providerRepo, &repo.PeerShare{
Name: "entry-share",
NodeID: providerEntryNodeID,
Token: "share-entry-token",
@@ -49,7 +49,7 @@ func TestFederationDualPanelMiddleExitAutoPortContract(t *testing.T) {
CreatedTime: now,
UpdatedTime: now,
})
middleShareID := insertPeerShare(t, providerRepo, &sqlite.PeerShare{
middleShareID := insertPeerShare(t, providerRepo, &repo.PeerShare{
Name: "middle-share",
NodeID: providerMiddleNodeID,
Token: "share-middle-token",
@@ -59,7 +59,7 @@ func TestFederationDualPanelMiddleExitAutoPortContract(t *testing.T) {
CreatedTime: now,
UpdatedTime: now,
})
exitShareID := insertPeerShare(t, providerRepo, &sqlite.PeerShare{
exitShareID := insertPeerShare(t, providerRepo, &repo.PeerShare{
Name: "exit-share",
NodeID: providerExitNodeID,
Token: "share-exit-token",
@@ -113,7 +113,7 @@ func TestFederationDualPanelMiddleExitAutoPortContract(t *testing.T) {
assertCode(t, res, 0)
var tunnelID int64
if err := consumerRepo.DB().QueryRow(`SELECT id FROM tunnel WHERE name = ? ORDER BY id DESC LIMIT 1`, name).Scan(&tunnelID); err != nil {
if err := consumerRepo.DB().Raw(`SELECT id FROM tunnel WHERE name = ? ORDER BY id DESC LIMIT 1`, name).Row().Scan(&tunnelID); err != nil {
t.Fatalf("query tunnel id (%s): %v", name, err)
}
if tunnelID <= 0 {
@@ -191,7 +191,7 @@ func TestFederationDualPanelRemoteDiagnosisContract(t *testing.T) {
providerMiddleNodeID := insertContractNode(t, providerRepo, "provider-middle-dx", "203.0.113.12", "54000-54010", "provider-middle-dx-secret", 1)
providerExitNodeID := insertContractNode(t, providerRepo, "provider-exit-dx", "203.0.113.13", "55000-55010", "provider-exit-dx-secret", 1)
entryShareID := insertPeerShare(t, providerRepo, &sqlite.PeerShare{
entryShareID := insertPeerShare(t, providerRepo, &repo.PeerShare{
Name: "entry-share-dx",
NodeID: providerEntryNodeID,
Token: "share-entry-dx-token",
@@ -201,7 +201,7 @@ func TestFederationDualPanelRemoteDiagnosisContract(t *testing.T) {
CreatedTime: now,
UpdatedTime: now,
})
middleShareID := insertPeerShare(t, providerRepo, &sqlite.PeerShare{
middleShareID := insertPeerShare(t, providerRepo, &repo.PeerShare{
Name: "middle-share-dx",
NodeID: providerMiddleNodeID,
Token: "share-middle-dx-token",
@@ -211,7 +211,7 @@ func TestFederationDualPanelRemoteDiagnosisContract(t *testing.T) {
CreatedTime: now,
UpdatedTime: now,
})
exitShareID := insertPeerShare(t, providerRepo, &sqlite.PeerShare{
exitShareID := insertPeerShare(t, providerRepo, &repo.PeerShare{
Name: "exit-share-dx",
NodeID: providerExitNodeID,
Token: "share-exit-dx-token",
@@ -262,7 +262,7 @@ func TestFederationDualPanelRemoteDiagnosisContract(t *testing.T) {
assertCode(t, createRes, 0)
var tunnelID int64
if err := consumerRepo.DB().QueryRow(`SELECT id FROM tunnel WHERE name = ? ORDER BY id DESC LIMIT 1`, "dual-panel-diagnose-remote").Scan(&tunnelID); err != nil {
if err := consumerRepo.DB().Raw(`SELECT id FROM tunnel WHERE name = ? ORDER BY id DESC LIMIT 1`, "dual-panel-diagnose-remote").Row().Scan(&tunnelID); err != nil {
t.Fatalf("query tunnel id: %v", err)
}
if tunnelID <= 0 {
@@ -335,7 +335,7 @@ func TestFederationDualPanelRemoteEntryRuntimeContract(t *testing.T) {
providerMiddleNodeID := insertContractNode(t, providerRepo, "provider-middle-rt", "198.51.100.22", "44020-44030", "provider-middle-rt-secret", 1)
providerExitNodeID := insertContractNode(t, providerRepo, "provider-exit-rt", "198.51.100.23", "45020-45030", "provider-exit-rt-secret", 1)
insertPeerShare(t, providerRepo, &sqlite.PeerShare{
insertPeerShare(t, providerRepo, &repo.PeerShare{
Name: "entry-share-rt",
NodeID: providerEntryNodeID,
Token: "share-entry-rt-token",
@@ -345,7 +345,7 @@ func TestFederationDualPanelRemoteEntryRuntimeContract(t *testing.T) {
CreatedTime: now,
UpdatedTime: now,
})
insertPeerShare(t, providerRepo, &sqlite.PeerShare{
insertPeerShare(t, providerRepo, &repo.PeerShare{
Name: "middle-share-rt",
NodeID: providerMiddleNodeID,
Token: "share-middle-rt-token",
@@ -355,7 +355,7 @@ func TestFederationDualPanelRemoteEntryRuntimeContract(t *testing.T) {
CreatedTime: now,
UpdatedTime: now,
})
insertPeerShare(t, providerRepo, &sqlite.PeerShare{
insertPeerShare(t, providerRepo, &repo.PeerShare{
Name: "exit-share-rt",
NodeID: providerExitNodeID,
Token: "share-exit-rt-token",
@@ -415,7 +415,7 @@ func TestFederationDualPanelRemoteEntryRuntimeContract(t *testing.T) {
assertCode(t, res, 0)
var tunnelID int64
if err := consumerRepo.DB().QueryRow(`SELECT id FROM tunnel WHERE name = ? ORDER BY id DESC LIMIT 1`, name).Scan(&tunnelID); err != nil {
if err := consumerRepo.DB().Raw(`SELECT id FROM tunnel WHERE name = ? ORDER BY id DESC LIMIT 1`, name).Row().Scan(&tunnelID); err != nil {
t.Fatalf("query tunnel id (%s): %v", name, err)
}
if tunnelID <= 0 {
@@ -446,32 +446,31 @@ func TestFederationDualPanelRemoteEntryRuntimeContract(t *testing.T) {
createTunnel("dual-panel-remote-entry-offline")
}
func insertContractNode(t *testing.T, repo *sqlite.Repository, name, ip, portRange, secret string, status int) int64 {
func insertContractNode(t *testing.T, r *repo.Repository, name, ip, portRange, secret string, status int) int64 {
t.Helper()
now := time.Now().UnixMilli()
res, err := repo.DB().Exec(`
if err := r.DB().Exec(`
INSERT INTO node(name, secret, server_ip, server_ip_v4, server_ip_v6, port, interface_name, version, http, tls, socks, created_time, updated_time, status, tcp_listen_addr, udp_listen_addr, inx)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, name, secret, ip, ip, "", portRange, "", "v1", 1, 1, 1, now, now, status, "[::]", "[::]", 0)
if err != nil {
`, name, secret, ip, ip, "", portRange, "", "v1", 1, 1, 1, now, now, status, "[::]", "[::]", 0).Error; err != nil {
t.Fatalf("insert node %s: %v", name, err)
}
id, err := res.LastInsertId()
if err != nil {
var id int64
if err := r.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&id); err != nil {
t.Fatalf("node id %s: %v", name, err)
}
return id
}
func insertPeerShare(t *testing.T, repo *sqlite.Repository, share *sqlite.PeerShare) int64 {
func insertPeerShare(t *testing.T, r *repo.Repository, share *repo.PeerShare) int64 {
t.Helper()
if share == nil {
t.Fatalf("share is nil")
}
if err := repo.CreatePeerShare(share); err != nil {
if err := r.CreatePeerShare(share); err != nil {
t.Fatalf("create peer share %s: %v", share.Name, err)
}
saved, err := repo.GetPeerShareByToken(share.Token)
saved, err := r.GetPeerShareByToken(share.Token)
if err != nil {
t.Fatalf("query peer share %s: %v", share.Name, err)
}
@@ -498,10 +497,10 @@ func importRemoteNodeForContract(t *testing.T, router http.Handler, adminToken,
assertCode(t, res, 0)
}
func queryRemoteNodeIDByToken(t *testing.T, repo *sqlite.Repository, token string) int64 {
func queryRemoteNodeIDByToken(t *testing.T, r *repo.Repository, token string) int64 {
t.Helper()
var id int64
if err := repo.DB().QueryRow(`SELECT id FROM node WHERE is_remote = 1 AND remote_token = ? ORDER BY id DESC LIMIT 1`, token).Scan(&id); err != nil {
if err := r.DB().Raw(`SELECT id FROM node WHERE is_remote = 1 AND remote_token = ? ORDER BY id DESC LIMIT 1`, token).Row().Scan(&id); err != nil {
t.Fatalf("query remote node by token %s: %v", token, err)
}
if id <= 0 {
@@ -510,15 +509,15 @@ func queryRemoteNodeIDByToken(t *testing.T, repo *sqlite.Repository, token strin
return id
}
func assertTunnelPortInRange(t *testing.T, repo *sqlite.Repository, tunnelID int64, chainType int, nodeID int64, minPort int, maxPort int) {
func assertTunnelPortInRange(t *testing.T, r *repo.Repository, tunnelID int64, chainType int, nodeID int64, minPort int, maxPort int) {
t.Helper()
var port int
err := repo.DB().QueryRow(`
err := r.DB().Raw(`
SELECT port
FROM chain_tunnel
WHERE tunnel_id = ? AND chain_type = ? AND node_id = ?
LIMIT 1
`, tunnelID, chainType, nodeID).Scan(&port)
`, tunnelID, chainType, nodeID).Row().Scan(&port)
if err != nil {
t.Fatalf("query tunnel=%d chainType=%d node=%d port: %v", tunnelID, chainType, nodeID, err)
}
@@ -527,10 +526,10 @@ func assertTunnelPortInRange(t *testing.T, repo *sqlite.Repository, tunnelID int
}
}
func assertCount(t *testing.T, repo *sqlite.Repository, query string, arg interface{}, expected int) {
func assertCount(t *testing.T, r *repo.Repository, query string, arg interface{}, expected int) {
t.Helper()
var got int
if err := repo.DB().QueryRow(query, arg).Scan(&got); err != nil {
if err := r.DB().Raw(query, arg).Row().Scan(&got); err != nil {
t.Fatalf("count query failed: %v", err)
}
if got != expected {
@@ -638,12 +637,12 @@ func startMockNodeSessionWithHook(t *testing.T, baseURL string, nodeSecret strin
}
}
func waitNodeStatus(t *testing.T, repo *sqlite.Repository, nodeID int64, expectedStatus int) {
func waitNodeStatus(t *testing.T, r *repo.Repository, nodeID int64, expectedStatus int) {
t.Helper()
deadline := time.Now().Add(2 * time.Second)
for {
var status int
if err := repo.DB().QueryRow(`SELECT status FROM node WHERE id = ?`, nodeID).Scan(&status); err == nil && status == expectedStatus {
if err := r.DB().Raw(`SELECT status FROM node WHERE id = ?`, nodeID).Row().Scan(&status); err == nil && status == expectedStatus {
return
}
if time.Now().After(deadline) {
@@ -698,7 +697,7 @@ func TestFederationRuntimeCommandPortRangeEnforcement(t *testing.T) {
now := time.Now().UnixMilli()
providerNodeID := insertContractNode(t, providerRepo, "provider-portrange-node", "198.51.100.50", "44000-44010", "provider-portrange-secret", 1)
insertPeerShare(t, providerRepo, &sqlite.PeerShare{
insertPeerShare(t, providerRepo, &repo.PeerShare{
Name: "portrange-share",
NodeID: providerNodeID,
Token: "share-portrange-token",
@@ -18,65 +18,61 @@ func TestForwardOwnershipAndScopeContracts(t *testing.T) {
router, repo := setupContractRouter(t, secret)
now := time.Now().UnixMilli()
if _, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO user(id, user, pwd, role_id, exp_time, flow, in_flow, out_flow, flow_reset_time, num, created_time, updated_time, status)
VALUES(2, 'normal_user', '3c85cdebade1c51cf64ca9f3c09d182d', 1, 2727251700000, 99999, 0, 0, 1, 99999, ?, ?, 1)
`, now, now); err != nil {
`, now, now).Error; err != nil {
t.Fatalf("insert user: %v", err)
}
res, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO tunnel(name, traffic_ratio, type, protocol, flow, created_time, updated_time, status, in_ip, inx)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, "contract-tunnel", 1.0, 1, "tls", 99999, now, now, 1, nil, 0)
if err != nil {
`, "contract-tunnel", 1.0, 1, "tls", 99999, now, now, 1, nil, 0).Error; err != nil {
t.Fatalf("insert tunnel: %v", err)
}
tunnelID, err := res.LastInsertId()
if err != nil {
var tunnelID int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&tunnelID); err != nil {
t.Fatalf("get tunnel id: %v", err)
}
nodeRes, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO node(name, secret, server_ip, server_ip_v4, server_ip_v6, port, interface_name, version, http, tls, socks, created_time, updated_time, status, tcp_listen_addr, udp_listen_addr, inx)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, "entry-node", "entry-secret", "10.0.0.10", "10.0.0.10", "", "20000-20010", "", "v1", 1, 1, 1, now, now, 1, "[::]", "[::]", 0)
if err != nil {
`, "entry-node", "entry-secret", "10.0.0.10", "10.0.0.10", "", "20000-20010", "", "v1", 1, 1, 1, now, now, 1, "[::]", "[::]", 0).Error; err != nil {
t.Fatalf("insert node: %v", err)
}
entryNodeID, err := nodeRes.LastInsertId()
if err != nil {
var entryNodeID int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&entryNodeID); err != nil {
t.Fatalf("get node id: %v", err)
}
if _, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO chain_tunnel(tunnel_id, chain_type, node_id, port, strategy, inx, protocol)
VALUES(?, 1, ?, 20001, 'round', 1, 'tls')
`, tunnelID, entryNodeID); err != nil {
`, tunnelID, entryNodeID).Error; err != nil {
t.Fatalf("insert chain_tunnel: %v", err)
}
resAdmin, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO forward(user_id, user_name, name, tunnel_id, remote_addr, strategy, in_flow, out_flow, created_time, updated_time, status, inx)
VALUES(?, ?, ?, ?, ?, ?, 0, 0, ?, ?, 1, ?)
`, 1, "admin_user", "admin-forward", tunnelID, "1.1.1.1:443", "fifo", now, now, 0)
if err != nil {
`, 1, "admin_user", "admin-forward", tunnelID, "1.1.1.1:443", "fifo", now, now, 0).Error; err != nil {
t.Fatalf("insert admin forward: %v", err)
}
adminForwardID, err := resAdmin.LastInsertId()
if err != nil {
var adminForwardID int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&adminForwardID); err != nil {
t.Fatalf("get admin forward id: %v", err)
}
resUser, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO forward(user_id, user_name, name, tunnel_id, remote_addr, strategy, in_flow, out_flow, created_time, updated_time, status, inx)
VALUES(?, ?, ?, ?, ?, ?, 0, 0, ?, ?, 1, ?)
`, 2, "normal_user", "user-forward", tunnelID, "8.8.8.8:53", "fifo", now, now, 1)
if err != nil {
`, 2, "normal_user", "user-forward", tunnelID, "8.8.8.8:53", "fifo", now, now, 1).Error; err != nil {
t.Fatalf("insert user forward: %v", err)
}
userForwardID, err := resUser.LastInsertId()
if err != nil {
var userForwardID int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&userForwardID); err != nil {
t.Fatalf("get user forward id: %v", err)
}
@@ -207,38 +203,36 @@ func TestForwardSwitchTunnelRollbackOnSyncFailure(t *testing.T) {
t.Fatalf("generate admin token: %v", err)
}
if _, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO user(id, user, pwd, role_id, exp_time, flow, in_flow, out_flow, flow_reset_time, num, created_time, updated_time, status)
VALUES(2, 'switch_user', '3c85cdebade1c51cf64ca9f3c09d182d', 1, 2727251700000, 99999, 0, 0, 1, 99999, ?, ?, 1)
`, now, now); err != nil {
`, now, now).Error; err != nil {
t.Fatalf("insert user: %v", err)
}
insertTunnel := func(name string, inx int) int64 {
res, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO tunnel(name, traffic_ratio, type, protocol, flow, created_time, updated_time, status, in_ip, inx)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, name, 1.0, 1, "tls", 99999, now, now, 1, nil, inx)
if err != nil {
`, name, 1.0, 1, "tls", 99999, now, now, 1, nil, inx).Error; err != nil {
t.Fatalf("insert tunnel %s: %v", name, err)
}
id, err := res.LastInsertId()
if err != nil {
var id int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&id); err != nil {
t.Fatalf("get tunnel id %s: %v", name, err)
}
return id
}
insertNode := func(name, ip, portRange string, inx int) int64 {
res, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO node(name, secret, server_ip, server_ip_v4, server_ip_v6, port, interface_name, version, http, tls, socks, created_time, updated_time, status, tcp_listen_addr, udp_listen_addr, inx)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, name, name+"-secret", ip, ip, "", portRange, "", "v1", 1, 1, 1, now, now, 1, "[::]", "[::]", inx)
if err != nil {
`, name, name+"-secret", ip, ip, "", portRange, "", "v1", 1, 1, 1, now, now, 1, "[::]", "[::]", inx).Error; err != nil {
t.Fatalf("insert node %s: %v", name, err)
}
id, err := res.LastInsertId()
if err != nil {
var id int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&id); err != nil {
t.Fatalf("get node id %s: %v", name, err)
}
return id
@@ -249,45 +243,44 @@ func TestForwardSwitchTunnelRollbackOnSyncFailure(t *testing.T) {
nodeA := insertNode("switch-node-a", "10.10.0.1", "21000-21010", 0)
nodeB := insertNode("switch-node-b", "10.10.0.2", "22000-22010", 1)
if _, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO chain_tunnel(tunnel_id, chain_type, node_id, port, strategy, inx, protocol)
VALUES(?, 1, ?, 21001, 'round', 1, 'tls')
`, tunnelA, nodeA); err != nil {
`, tunnelA, nodeA).Error; err != nil {
t.Fatalf("insert chain_tunnel tunnelA: %v", err)
}
if _, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO chain_tunnel(tunnel_id, chain_type, node_id, port, strategy, inx, protocol)
VALUES(?, 1, ?, 22001, 'round', 1, 'tls')
`, tunnelB, nodeB); err != nil {
`, tunnelB, nodeB).Error; err != nil {
t.Fatalf("insert chain_tunnel tunnelB: %v", err)
}
if _, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO user_tunnel(id, user_id, tunnel_id, speed_id, num, flow, in_flow, out_flow, flow_reset_time, exp_time, status)
VALUES(10, 2, ?, NULL, 999, 99999, 0, 0, 1, 2727251700000, 1)
`, tunnelA); err != nil {
`, tunnelA).Error; err != nil {
t.Fatalf("insert user_tunnel A: %v", err)
}
if _, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO user_tunnel(id, user_id, tunnel_id, speed_id, num, flow, in_flow, out_flow, flow_reset_time, exp_time, status)
VALUES(11, 2, ?, NULL, 999, 99999, 0, 0, 1, 2727251700000, 1)
`, tunnelB); err != nil {
`, tunnelB).Error; err != nil {
t.Fatalf("insert user_tunnel B: %v", err)
}
forwardRes, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO forward(user_id, user_name, name, tunnel_id, remote_addr, strategy, in_flow, out_flow, created_time, updated_time, status, inx)
VALUES(2, 'switch_user', 'switch-forward', ?, '8.8.8.8:53', 'fifo', 0, 0, ?, ?, 1, 0)
`, tunnelA, now, now)
if err != nil {
`, tunnelA, now, now).Error; err != nil {
t.Fatalf("insert forward: %v", err)
}
forwardID, err := forwardRes.LastInsertId()
if err != nil {
var forwardID int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&forwardID); err != nil {
t.Fatalf("get forward id: %v", err)
}
if _, err := repo.DB().Exec(`INSERT INTO forward_port(forward_id, node_id, port) VALUES(?, ?, ?)`, forwardID, nodeA, 21001); err != nil {
if err := repo.DB().Exec(`INSERT INTO forward_port(forward_id, node_id, port) VALUES(?, ?, ?)`, forwardID, nodeA, 21001).Error; err != nil {
t.Fatalf("insert forward_port: %v", err)
}
@@ -308,7 +301,7 @@ func TestForwardSwitchTunnelRollbackOnSyncFailure(t *testing.T) {
}
var tunnelAfter int64
if err := repo.DB().QueryRow(`SELECT tunnel_id FROM forward WHERE id = ?`, forwardID).Scan(&tunnelAfter); err != nil {
if err := repo.DB().Raw(`SELECT tunnel_id FROM forward WHERE id = ?`, forwardID).Row().Scan(&tunnelAfter); err != nil {
t.Fatalf("query forward tunnel_id: %v", err)
}
if tunnelAfter != tunnelA {
@@ -317,7 +310,7 @@ func TestForwardSwitchTunnelRollbackOnSyncFailure(t *testing.T) {
var nodeAfter int64
var portAfter int
if err := repo.DB().QueryRow(`SELECT node_id, port FROM forward_port WHERE forward_id = ? LIMIT 1`, forwardID).Scan(&nodeAfter, &portAfter); err != nil {
if err := repo.DB().Raw(`SELECT node_id, port FROM forward_port WHERE forward_id = ? LIMIT 1`, forwardID).Row().Scan(&nodeAfter, &portAfter); err != nil {
t.Fatalf("query forward_port: %v", err)
}
if nodeAfter != nodeA || portAfter != 21001 {
@@ -335,73 +328,83 @@ func TestForwardBatchChangeTunnelRollbackOnSyncFailure(t *testing.T) {
t.Fatalf("generate admin token: %v", err)
}
if _, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO user(id, user, pwd, role_id, exp_time, flow, in_flow, out_flow, flow_reset_time, num, created_time, updated_time, status)
VALUES(2, 'batch_switch_user', '3c85cdebade1c51cf64ca9f3c09d182d', 1, 2727251700000, 99999, 0, 0, 1, 99999, ?, ?, 1)
`, now, now); err != nil {
`, now, now).Error; err != nil {
t.Fatalf("insert user: %v", err)
}
tunnelResA, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO tunnel(name, traffic_ratio, type, protocol, flow, created_time, updated_time, status, in_ip, inx)
VALUES('batch-switch-tunnel-a', 1.0, 1, 'tls', 99999, ?, ?, 1, NULL, 0)
`, now, now)
if err != nil {
`, now, now).Error; err != nil {
t.Fatalf("insert tunnel A: %v", err)
}
tunnelA, _ := tunnelResA.LastInsertId()
var tunnelA int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&tunnelA); err != nil {
t.Fatal(err)
}
tunnelResB, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO tunnel(name, traffic_ratio, type, protocol, flow, created_time, updated_time, status, in_ip, inx)
VALUES('batch-switch-tunnel-b', 1.0, 1, 'tls', 99999, ?, ?, 1, NULL, 1)
`, now, now)
if err != nil {
`, now, now).Error; err != nil {
t.Fatalf("insert tunnel B: %v", err)
}
tunnelB, _ := tunnelResB.LastInsertId()
var tunnelB int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&tunnelB); err != nil {
t.Fatal(err)
}
nodeResA, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO node(name, secret, server_ip, server_ip_v4, server_ip_v6, port, interface_name, version, http, tls, socks, created_time, updated_time, status, tcp_listen_addr, udp_listen_addr, inx)
VALUES('batch-switch-node-a', 'batch-switch-node-a-secret', '10.11.0.1', '10.11.0.1', '', '23000-23010', '', 'v1', 1, 1, 1, ?, ?, 1, '[::]', '[::]', 0)
`, now, now)
if err != nil {
`, now, now).Error; err != nil {
t.Fatalf("insert node A: %v", err)
}
nodeA, _ := nodeResA.LastInsertId()
var nodeA int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&nodeA); err != nil {
t.Fatal(err)
}
nodeResB, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO node(name, secret, server_ip, server_ip_v4, server_ip_v6, port, interface_name, version, http, tls, socks, created_time, updated_time, status, tcp_listen_addr, udp_listen_addr, inx)
VALUES('batch-switch-node-b', 'batch-switch-node-b-secret', '10.11.0.2', '10.11.0.2', '', '24000-24010', '', 'v1', 1, 1, 1, ?, ?, 1, '[::]', '[::]', 1)
`, now, now)
if err != nil {
`, now, now).Error; err != nil {
t.Fatalf("insert node B: %v", err)
}
nodeB, _ := nodeResB.LastInsertId()
var nodeB int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&nodeB); err != nil {
t.Fatal(err)
}
if _, err := repo.DB().Exec(`INSERT INTO chain_tunnel(tunnel_id, chain_type, node_id, port, strategy, inx, protocol) VALUES(?, 1, ?, 23001, 'round', 1, 'tls')`, tunnelA, nodeA); err != nil {
if err := repo.DB().Exec(`INSERT INTO chain_tunnel(tunnel_id, chain_type, node_id, port, strategy, inx, protocol) VALUES(?, 1, ?, 23001, 'round', 1, 'tls')`, tunnelA, nodeA).Error; err != nil {
t.Fatalf("insert chain_tunnel A: %v", err)
}
if _, err := repo.DB().Exec(`INSERT INTO chain_tunnel(tunnel_id, chain_type, node_id, port, strategy, inx, protocol) VALUES(?, 1, ?, 24001, 'round', 1, 'tls')`, tunnelB, nodeB); err != nil {
if err := repo.DB().Exec(`INSERT INTO chain_tunnel(tunnel_id, chain_type, node_id, port, strategy, inx, protocol) VALUES(?, 1, ?, 24001, 'round', 1, 'tls')`, tunnelB, nodeB).Error; err != nil {
t.Fatalf("insert chain_tunnel B: %v", err)
}
if _, err := repo.DB().Exec(`INSERT INTO user_tunnel(id, user_id, tunnel_id, speed_id, num, flow, in_flow, out_flow, flow_reset_time, exp_time, status) VALUES(20, 2, ?, NULL, 999, 99999, 0, 0, 1, 2727251700000, 1)`, tunnelA); err != nil {
if err := repo.DB().Exec(`INSERT INTO user_tunnel(id, user_id, tunnel_id, speed_id, num, flow, in_flow, out_flow, flow_reset_time, exp_time, status) VALUES(20, 2, ?, NULL, 999, 99999, 0, 0, 1, 2727251700000, 1)`, tunnelA).Error; err != nil {
t.Fatalf("insert user_tunnel A: %v", err)
}
if _, err := repo.DB().Exec(`INSERT INTO user_tunnel(id, user_id, tunnel_id, speed_id, num, flow, in_flow, out_flow, flow_reset_time, exp_time, status) VALUES(21, 2, ?, NULL, 999, 99999, 0, 0, 1, 2727251700000, 1)`, tunnelB); err != nil {
if err := repo.DB().Exec(`INSERT INTO user_tunnel(id, user_id, tunnel_id, speed_id, num, flow, in_flow, out_flow, flow_reset_time, exp_time, status) VALUES(21, 2, ?, NULL, 999, 99999, 0, 0, 1, 2727251700000, 1)`, tunnelB).Error; err != nil {
t.Fatalf("insert user_tunnel B: %v", err)
}
forwardRes, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO forward(user_id, user_name, name, tunnel_id, remote_addr, strategy, in_flow, out_flow, created_time, updated_time, status, inx)
VALUES(2, 'batch_switch_user', 'batch-switch-forward', ?, '1.1.1.1:443', 'fifo', 0, 0, ?, ?, 1, 0)
`, tunnelA, now, now)
if err != nil {
`, tunnelA, now, now).Error; err != nil {
t.Fatalf("insert forward: %v", err)
}
forwardID, _ := forwardRes.LastInsertId()
var forwardID int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&forwardID); err != nil {
t.Fatal(err)
}
if _, err := repo.DB().Exec(`INSERT INTO forward_port(forward_id, node_id, port) VALUES(?, ?, ?)`, forwardID, nodeA, 23001); err != nil {
if err := repo.DB().Exec(`INSERT INTO forward_port(forward_id, node_id, port) VALUES(?, ?, ?)`, forwardID, nodeA, 23001).Error; err != nil {
t.Fatalf("insert forward_port: %v", err)
}
@@ -430,7 +433,7 @@ func TestForwardBatchChangeTunnelRollbackOnSyncFailure(t *testing.T) {
}
var tunnelAfter int64
if err := repo.DB().QueryRow(`SELECT tunnel_id FROM forward WHERE id = ?`, forwardID).Scan(&tunnelAfter); err != nil {
if err := repo.DB().Raw(`SELECT tunnel_id FROM forward WHERE id = ?`, forwardID).Row().Scan(&tunnelAfter); err != nil {
t.Fatalf("query forward tunnel_id: %v", err)
}
if tunnelAfter != tunnelA {
@@ -439,7 +442,7 @@ func TestForwardBatchChangeTunnelRollbackOnSyncFailure(t *testing.T) {
var nodeAfter int64
var portAfter int
if err := repo.DB().QueryRow(`SELECT node_id, port FROM forward_port WHERE forward_id = ? LIMIT 1`, forwardID).Scan(&nodeAfter, &portAfter); err != nil {
if err := repo.DB().Raw(`SELECT node_id, port FROM forward_port WHERE forward_id = ? LIMIT 1`, forwardID).Row().Scan(&nodeAfter, &portAfter); err != nil {
t.Fatalf("query forward_port: %v", err)
}
if nodeAfter != nodeA || portAfter != 23001 {
@@ -457,21 +460,23 @@ func TestUserTunnelReassignmentKeepsStableID(t *testing.T) {
t.Fatalf("generate admin token: %v", err)
}
if _, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO user(id, user, pwd, role_id, exp_time, flow, in_flow, out_flow, flow_reset_time, num, created_time, updated_time, status)
VALUES(100, 'stable_user', 'pwd', 1, 2727251700000, 99999, 0, 0, 1, 99999, ?, ?, 1)
`, now, now); err != nil {
`, now, now).Error; err != nil {
t.Fatalf("insert user: %v", err)
}
tunnelRes, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO tunnel(name, traffic_ratio, type, protocol, flow, created_time, updated_time, status, in_ip, inx)
VALUES('stable-tunnel', 1.0, 1, 'tls', 99999, ?, ?, 1, NULL, 0)
`, now, now)
if err != nil {
`, now, now).Error; err != nil {
t.Fatalf("insert tunnel: %v", err)
}
tunnelID, _ := tunnelRes.LastInsertId()
var tunnelID int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&tunnelID); err != nil {
t.Fatal(err)
}
// 1. Assign permission (creates new user_tunnel)
// userTunnelBatchAssign expects structure: {userId: 123, tunnels: [{tunnelId: 456, ...}]}
@@ -491,7 +496,7 @@ func TestUserTunnelReassignmentKeepsStableID(t *testing.T) {
}
var initialID int64
if err := repo.DB().QueryRow(`SELECT id FROM user_tunnel WHERE user_id = 100 AND tunnel_id = ?`, tunnelID).Scan(&initialID); err != nil {
if err := repo.DB().Raw(`SELECT id FROM user_tunnel WHERE user_id = 100 AND tunnel_id = ?`, tunnelID).Row().Scan(&initialID); err != nil {
t.Fatalf("query initial user_tunnel id: %v", err)
}
@@ -513,7 +518,7 @@ func TestUserTunnelReassignmentKeepsStableID(t *testing.T) {
// 3. Verify stable ID and no duplicates
var count int
if err := repo.DB().QueryRow(`SELECT COUNT(1) FROM user_tunnel WHERE user_id = 100 AND tunnel_id = ?`, tunnelID).Scan(&count); err != nil {
if err := repo.DB().Raw(`SELECT COUNT(1) FROM user_tunnel WHERE user_id = 100 AND tunnel_id = ?`, tunnelID).Row().Scan(&count); err != nil {
t.Fatalf("query count: %v", err)
}
if count != 1 {
@@ -521,7 +526,7 @@ func TestUserTunnelReassignmentKeepsStableID(t *testing.T) {
}
var currentID int64
if err := repo.DB().QueryRow(`SELECT id FROM user_tunnel WHERE user_id = 100 AND tunnel_id = ?`, tunnelID).Scan(&currentID); err != nil {
if err := repo.DB().Raw(`SELECT id FROM user_tunnel WHERE user_id = 100 AND tunnel_id = ?`, tunnelID).Row().Scan(&currentID); err != nil {
t.Fatalf("query current user_tunnel: %v", err)
}
@@ -15,47 +15,44 @@ func TestGroupUserUnbindRevokesInheritedTunnelPermission(t *testing.T) {
router, repo := setupContractRouter(t, secret)
now := time.Now().UnixMilli()
if _, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO user(id, user, pwd, role_id, exp_time, flow, in_flow, out_flow, flow_reset_time, num, created_time, updated_time, status)
VALUES(200, 'group_user_contract', '3c85cdebade1c51cf64ca9f3c09d182d', 1, 2727251700000, 99999, 0, 0, 1, 99999, ?, ?, 1)
`, now, now); err != nil {
`, now, now).Error; err != nil {
t.Fatalf("insert test user: %v", err)
}
tunnelRes, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO tunnel(name, traffic_ratio, type, protocol, flow, created_time, updated_time, status, in_ip, inx)
VALUES('group-contract-tunnel', 1.0, 1, 'tls', 99999, ?, ?, 1, NULL, 0)
`, now, now)
if err != nil {
`, now, now).Error; err != nil {
t.Fatalf("insert tunnel: %v", err)
}
tunnelID, err := tunnelRes.LastInsertId()
if err != nil {
var tunnelID int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&tunnelID); err != nil {
t.Fatalf("read tunnel id: %v", err)
}
ugRes, err := repo.DB().Exec(`INSERT INTO user_group(name, created_time, updated_time, status) VALUES('ug-contract', ?, ?, 1)`, now, now)
if err != nil {
if err := repo.DB().Exec(`INSERT INTO user_group(name, created_time, updated_time, status) VALUES('ug-contract', ?, ?, 1)`, now, now).Error; err != nil {
t.Fatalf("insert user_group: %v", err)
}
userGroupID, err := ugRes.LastInsertId()
if err != nil {
var userGroupID int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&userGroupID); err != nil {
t.Fatalf("read user_group id: %v", err)
}
tgRes, err := repo.DB().Exec(`INSERT INTO tunnel_group(name, created_time, updated_time, status) VALUES('tg-contract', ?, ?, 1)`, now, now)
if err != nil {
if err := repo.DB().Exec(`INSERT INTO tunnel_group(name, created_time, updated_time, status) VALUES('tg-contract', ?, ?, 1)`, now, now).Error; err != nil {
t.Fatalf("insert tunnel_group: %v", err)
}
tunnelGroupID, err := tgRes.LastInsertId()
if err != nil {
var tunnelGroupID int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&tunnelGroupID); err != nil {
t.Fatalf("read tunnel_group id: %v", err)
}
if _, err := repo.DB().Exec(`INSERT INTO tunnel_group_tunnel(tunnel_group_id, tunnel_id, created_time) VALUES(?, ?, ?)`, tunnelGroupID, tunnelID, now); err != nil {
if err := repo.DB().Exec(`INSERT INTO tunnel_group_tunnel(tunnel_group_id, tunnel_id, created_time) VALUES(?, ?, ?)`, tunnelGroupID, tunnelID, now).Error; err != nil {
t.Fatalf("insert tunnel_group_tunnel: %v", err)
}
if _, err := repo.DB().Exec(`INSERT INTO group_permission(user_group_id, tunnel_group_id, created_time) VALUES(?, ?, ?)`, userGroupID, tunnelGroupID, now); err != nil {
if err := repo.DB().Exec(`INSERT INTO group_permission(user_group_id, tunnel_group_id, created_time) VALUES(?, ?, ?)`, userGroupID, tunnelGroupID, now).Error; err != nil {
t.Fatalf("insert group_permission: %v", err)
}
@@ -71,12 +68,12 @@ func TestGroupUserUnbindRevokesInheritedTunnelPermission(t *testing.T) {
assertCode(t, bindRes, 0)
var userTunnelID int64
if err := repo.DB().QueryRow(`SELECT id FROM user_tunnel WHERE user_id = 200 AND tunnel_id = ?`, tunnelID).Scan(&userTunnelID); err != nil {
if err := repo.DB().Raw(`SELECT id FROM user_tunnel WHERE user_id = 200 AND tunnel_id = ?`, tunnelID).Row().Scan(&userTunnelID); err != nil {
t.Fatalf("query user_tunnel after bind: %v", err)
}
var grantCount int
if err := repo.DB().QueryRow(`SELECT COUNT(1) FROM group_permission_grant WHERE user_tunnel_id = ?`, userTunnelID).Scan(&grantCount); err != nil {
if err := repo.DB().Raw(`SELECT COUNT(1) FROM group_permission_grant WHERE user_tunnel_id = ?`, userTunnelID).Row().Scan(&grantCount); err != nil {
t.Fatalf("query group_permission_grant after bind: %v", err)
}
if grantCount == 0 {
@@ -89,7 +86,7 @@ func TestGroupUserUnbindRevokesInheritedTunnelPermission(t *testing.T) {
router.ServeHTTP(unbindRes, unbindReq)
assertCode(t, unbindRes, 0)
if err := repo.DB().QueryRow(`SELECT COUNT(1) FROM group_permission_grant WHERE user_tunnel_id = ?`, userTunnelID).Scan(&grantCount); err != nil {
if err := repo.DB().Raw(`SELECT COUNT(1) FROM group_permission_grant WHERE user_tunnel_id = ?`, userTunnelID).Row().Scan(&grantCount); err != nil {
t.Fatalf("query group_permission_grant after unbind: %v", err)
}
if grantCount != 0 {
@@ -97,7 +94,7 @@ func TestGroupUserUnbindRevokesInheritedTunnelPermission(t *testing.T) {
}
var userTunnelCount int
if err := repo.DB().QueryRow(`SELECT COUNT(1) FROM user_tunnel WHERE id = ?`, userTunnelID).Scan(&userTunnelCount); err != nil {
if err := repo.DB().Raw(`SELECT COUNT(1) FROM user_tunnel WHERE id = ?`, userTunnelID).Row().Scan(&userTunnelCount); err != nil {
t.Fatalf("query user_tunnel after unbind: %v", err)
}
if userTunnelCount != 0 {
@@ -110,40 +107,37 @@ func TestGroupPermissionRemoveRevokesInheritedTunnelPermission(t *testing.T) {
router, repo := setupContractRouter(t, secret)
now := time.Now().UnixMilli()
if _, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO user(id, user, pwd, role_id, exp_time, flow, in_flow, out_flow, flow_reset_time, num, created_time, updated_time, status)
VALUES(201, 'group_user_permission_remove', '3c85cdebade1c51cf64ca9f3c09d182d', 1, 2727251700000, 99999, 0, 0, 1, 99999, ?, ?, 1)
`, now, now); err != nil {
`, now, now).Error; err != nil {
t.Fatalf("insert test user: %v", err)
}
tunnelRes, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO tunnel(name, traffic_ratio, type, protocol, flow, created_time, updated_time, status, in_ip, inx)
VALUES('group-remove-tunnel', 1.0, 1, 'tls', 99999, ?, ?, 1, NULL, 0)
`, now, now)
if err != nil {
`, now, now).Error; err != nil {
t.Fatalf("insert tunnel: %v", err)
}
tunnelID, err := tunnelRes.LastInsertId()
if err != nil {
var tunnelID int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&tunnelID); err != nil {
t.Fatalf("read tunnel id: %v", err)
}
ugRes, err := repo.DB().Exec(`INSERT INTO user_group(name, created_time, updated_time, status) VALUES('ug-remove-contract', ?, ?, 1)`, now, now)
if err != nil {
if err := repo.DB().Exec(`INSERT INTO user_group(name, created_time, updated_time, status) VALUES('ug-remove-contract', ?, ?, 1)`, now, now).Error; err != nil {
t.Fatalf("insert user_group: %v", err)
}
userGroupID, err := ugRes.LastInsertId()
if err != nil {
var userGroupID int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&userGroupID); err != nil {
t.Fatalf("read user_group id: %v", err)
}
tgRes, err := repo.DB().Exec(`INSERT INTO tunnel_group(name, created_time, updated_time, status) VALUES('tg-remove-contract', ?, ?, 1)`, now, now)
if err != nil {
if err := repo.DB().Exec(`INSERT INTO tunnel_group(name, created_time, updated_time, status) VALUES('tg-remove-contract', ?, ?, 1)`, now, now).Error; err != nil {
t.Fatalf("insert tunnel_group: %v", err)
}
tunnelGroupID, err := tgRes.LastInsertId()
if err != nil {
var tunnelGroupID int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&tunnelGroupID); err != nil {
t.Fatalf("read tunnel_group id: %v", err)
}
@@ -171,17 +165,17 @@ func TestGroupPermissionRemoveRevokesInheritedTunnelPermission(t *testing.T) {
assertCode(t, assignPermissionRes, 0)
var permissionID int64
if err := repo.DB().QueryRow(`SELECT id FROM group_permission WHERE user_group_id = ? AND tunnel_group_id = ?`, userGroupID, tunnelGroupID).Scan(&permissionID); err != nil {
if err := repo.DB().Raw(`SELECT id FROM group_permission WHERE user_group_id = ? AND tunnel_group_id = ?`, userGroupID, tunnelGroupID).Row().Scan(&permissionID); err != nil {
t.Fatalf("query group_permission id: %v", err)
}
var userTunnelID int64
if err := repo.DB().QueryRow(`SELECT id FROM user_tunnel WHERE user_id = 201 AND tunnel_id = ?`, tunnelID).Scan(&userTunnelID); err != nil {
if err := repo.DB().Raw(`SELECT id FROM user_tunnel WHERE user_id = 201 AND tunnel_id = ?`, tunnelID).Row().Scan(&userTunnelID); err != nil {
t.Fatalf("query user_tunnel after assign: %v", err)
}
var grantCount int
if err := repo.DB().QueryRow(`SELECT COUNT(1) FROM group_permission_grant WHERE user_tunnel_id = ?`, userTunnelID).Scan(&grantCount); err != nil {
if err := repo.DB().Raw(`SELECT COUNT(1) FROM group_permission_grant WHERE user_tunnel_id = ?`, userTunnelID).Row().Scan(&grantCount); err != nil {
t.Fatalf("query group_permission_grant after assign: %v", err)
}
if grantCount == 0 {
@@ -195,14 +189,14 @@ func TestGroupPermissionRemoveRevokesInheritedTunnelPermission(t *testing.T) {
assertCode(t, removeRes, 0)
var permissionCount int
if err := repo.DB().QueryRow(`SELECT COUNT(1) FROM group_permission WHERE id = ?`, permissionID).Scan(&permissionCount); err != nil {
if err := repo.DB().Raw(`SELECT COUNT(1) FROM group_permission WHERE id = ?`, permissionID).Row().Scan(&permissionCount); err != nil {
t.Fatalf("query group_permission after remove: %v", err)
}
if permissionCount != 0 {
t.Fatalf("expected group_permission removed, got %d", permissionCount)
}
if err := repo.DB().QueryRow(`SELECT COUNT(1) FROM group_permission_grant WHERE user_tunnel_id = ?`, userTunnelID).Scan(&grantCount); err != nil {
if err := repo.DB().Raw(`SELECT COUNT(1) FROM group_permission_grant WHERE user_tunnel_id = ?`, userTunnelID).Row().Scan(&grantCount); err != nil {
t.Fatalf("query group_permission_grant after remove: %v", err)
}
if grantCount != 0 {
@@ -210,7 +204,7 @@ func TestGroupPermissionRemoveRevokesInheritedTunnelPermission(t *testing.T) {
}
var userTunnelCount int
if err := repo.DB().QueryRow(`SELECT COUNT(1) FROM user_tunnel WHERE id = ?`, userTunnelID).Scan(&userTunnelCount); err != nil {
if err := repo.DB().Raw(`SELECT COUNT(1) FROM user_tunnel WHERE id = ?`, userTunnelID).Row().Scan(&userTunnelCount); err != nil {
t.Fatalf("query user_tunnel after permission remove: %v", err)
}
if userTunnelCount != 0 {
@@ -17,22 +17,20 @@ import (
httpserver "go-backend/internal/http"
"go-backend/internal/http/handler"
"go-backend/internal/http/response"
"go-backend/internal/store"
"go-backend/internal/store/sqlite"
"go-backend/internal/store/repo"
_ "modernc.org/sqlite"
"gorm.io/gorm"
)
func TestCaptchaVerifyLoginContract(t *testing.T) {
secret := "contract-jwt-secret"
router, repo := setupContractRouter(t, secret)
router, r := setupContractRouter(t, secret)
_, err := repo.DB().Exec(`
if err := r.DB().Exec(`
INSERT INTO vite_config(name, value, time)
VALUES(?, ?, ?)
ON CONFLICT(name) DO UPDATE SET value = excluded.value, time = excluded.time
`, "captcha_enabled", "true", time.Now().UnixMilli())
if err != nil {
`, "captcha_enabled", "true", time.Now().UnixMilli()).Error; err != nil {
t.Fatalf("enable captcha: %v", err)
}
@@ -84,7 +82,7 @@ func TestCaptchaVerifyLoginContract(t *testing.T) {
}
func TestOpenAPISubStoreContracts(t *testing.T) {
router, repo := setupContractRouter(t, "contract-jwt-secret")
router, r := setupContractRouter(t, "contract-jwt-secret")
const tunnelFlowGB = int64(500)
const tunnelInFlow = int64(123)
@@ -92,17 +90,16 @@ func TestOpenAPISubStoreContracts(t *testing.T) {
const tunnelExpTimeMs = int64(2727251700000)
now := time.Now().UnixMilli()
res, err := repo.DB().Exec(`INSERT INTO tunnel(name, traffic_ratio, type, protocol, flow, created_time, updated_time, status, in_ip, inx) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
"contract-tunnel", 1.0, 1, "tls", 1, now, now, 1, nil, 0)
if err != nil {
if err := r.DB().Exec(`INSERT INTO tunnel(name, traffic_ratio, type, protocol, flow, created_time, updated_time, status, in_ip, inx) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
"contract-tunnel", 1.0, 1, "tls", 1, now, now, 1, nil, 0).Error; err != nil {
t.Fatalf("insert tunnel: %v", err)
}
tunnelID, err := res.LastInsertId()
if err != nil {
var tunnelID int64
if err := r.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&tunnelID); err != nil {
t.Fatalf("last insert id: %v", err)
}
if _, err := repo.DB().Exec(`INSERT INTO user_tunnel(user_id, tunnel_id, speed_id, num, flow, in_flow, out_flow, flow_reset_time, exp_time, status) VALUES(?, ?, NULL, ?, ?, ?, ?, ?, ?, ?)`,
1, tunnelID, 99999, tunnelFlowGB, tunnelInFlow, tunnelOutFlow, 1, tunnelExpTimeMs, 1); err != nil {
if err := r.DB().Exec(`INSERT INTO user_tunnel(user_id, tunnel_id, speed_id, num, flow, in_flow, out_flow, flow_reset_time, exp_time, status) VALUES(?, ?, NULL, ?, ?, ?, ?, ?, ?, ?)`,
1, tunnelID, 99999, tunnelFlowGB, tunnelInFlow, tunnelOutFlow, 1, tunnelExpTimeMs, 1).Error; err != nil {
t.Fatalf("insert user_tunnel: %v", err)
}
@@ -205,7 +202,7 @@ func TestSpeedLimitTunnelsRouteAlias(t *testing.T) {
func TestBackupExportImportRestoreContracts(t *testing.T) {
secret := "contract-jwt-secret"
router, repo := setupContractRouter(t, secret)
router, r := setupContractRouter(t, secret)
adminToken, err := auth.GenerateToken(1, "admin_user", 0, secret)
if err != nil {
@@ -217,11 +214,11 @@ func TestBackupExportImportRestoreContracts(t *testing.T) {
}
key := "backup_contract_key"
if _, err := repo.DB().Exec(`
if err := r.DB().Exec(`
INSERT INTO vite_config(name, value, time)
VALUES(?, ?, ?)
ON CONFLICT(name) DO UPDATE SET value = excluded.value, time = excluded.time
`, key, "v1", time.Now().UnixMilli()); err != nil {
`, key, "v1", time.Now().UnixMilli()).Error; err != nil {
t.Fatalf("seed config for backup contract: %v", err)
}
@@ -271,7 +268,7 @@ func TestBackupExportImportRestoreContracts(t *testing.T) {
t.Fatalf("expected import code 0, got %d (%s)", out.Code, out.Msg)
}
cfg, err := repo.GetConfigByName(key)
cfg, err := r.GetConfigByName(key)
if err != nil {
t.Fatalf("query imported config: %v", err)
}
@@ -302,7 +299,7 @@ func TestBackupExportImportRestoreContracts(t *testing.T) {
t.Fatalf("expected restore code 0, got %d (%s)", out.Code, out.Msg)
}
cfg, err := repo.GetConfigByName(key)
cfg, err := r.GetConfigByName(key)
if err != nil {
t.Fatalf("query restored config: %v", err)
}
@@ -314,27 +311,25 @@ func TestBackupExportImportRestoreContracts(t *testing.T) {
t.Run("backup export and import preserve forward ports", func(t *testing.T) {
now := time.Now().UnixMilli()
tunnelRes, err := repo.DB().Exec(`
if err := r.DB().Exec(`
INSERT INTO tunnel(name, traffic_ratio, type, protocol, flow, created_time, updated_time, status, in_ip, inx)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, "backup-forward-tunnel", 1.0, 1, "tls", 0, now, now, 1, "", 88)
if err != nil {
`, "backup-forward-tunnel", 1.0, 1, "tls", 0, now, now, 1, "", 88).Error; err != nil {
t.Fatalf("seed tunnel for forward backup: %v", err)
}
tunnelID, err := tunnelRes.LastInsertId()
if err != nil {
var tunnelID int64
if err := r.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&tunnelID); err != nil {
t.Fatalf("read tunnel id for forward backup: %v", err)
}
forwardRes, err := repo.DB().Exec(`
if err := r.DB().Exec(`
INSERT INTO forward(user_id, user_name, name, tunnel_id, remote_addr, strategy, in_flow, out_flow, created_time, updated_time, status, inx)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, 1, "admin_user", "backup-forward", tunnelID, "127.0.0.1:9000", "fifo", 0, 0, now, now, 1, 88)
if err != nil {
`, 1, "admin_user", "backup-forward", tunnelID, "127.0.0.1:9000", "fifo", 0, 0, now, now, 1, 88).Error; err != nil {
t.Fatalf("seed forward for backup: %v", err)
}
forwardID, err := forwardRes.LastInsertId()
if err != nil {
var forwardID int64
if err := r.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&forwardID); err != nil {
t.Fatalf("read forward id for backup: %v", err)
}
@@ -343,7 +338,7 @@ func TestBackupExportImportRestoreContracts(t *testing.T) {
2002: 21002,
}
for nodeID, port := range expected {
if _, err := repo.DB().Exec(`INSERT INTO forward_port(forward_id, node_id, port) VALUES(?, ?, ?)`, forwardID, nodeID, port); err != nil {
if err := r.DB().Exec(`INSERT INTO forward_port(forward_id, node_id, port) VALUES(?, ?, ?)`, forwardID, nodeID, port).Error; err != nil {
t.Fatalf("seed forward_port %d:%d: %v", nodeID, port, err)
}
}
@@ -420,10 +415,10 @@ func TestBackupExportImportRestoreContracts(t *testing.T) {
}
}
if _, err := repo.DB().Exec(`DELETE FROM forward_port WHERE forward_id = ?`, forwardID); err != nil {
if err := r.DB().Exec(`DELETE FROM forward_port WHERE forward_id = ?`, forwardID).Error; err != nil {
t.Fatalf("clear forward_port before import: %v", err)
}
if _, err := repo.DB().Exec(`INSERT INTO forward_port(forward_id, node_id, port) VALUES(?, ?, ?)`, forwardID, 9999, 39999); err != nil {
if err := r.DB().Exec(`INSERT INTO forward_port(forward_id, node_id, port) VALUES(?, ?, ?)`, forwardID, 9999, 39999).Error; err != nil {
t.Fatalf("seed wrong forward_port before import: %v", err)
}
@@ -447,7 +442,7 @@ func TestBackupExportImportRestoreContracts(t *testing.T) {
t.Fatalf("expected forwards import code 0, got %d (%s)", out.Code, out.Msg)
}
rows, err := repo.DB().Query(`SELECT node_id, port FROM forward_port WHERE forward_id = ? ORDER BY id ASC`, forwardID)
rows, err := r.DB().Raw(`SELECT node_id, port FROM forward_port WHERE forward_id = ? ORDER BY id ASC`, forwardID).Rows()
if err != nil {
t.Fatalf("query forward ports after import: %v", err)
}
@@ -478,22 +473,21 @@ func TestBackupExportImportRestoreContracts(t *testing.T) {
t.Run("backup export tolerates nullable legacy tunnel chain fields", func(t *testing.T) {
now := time.Now().UnixMilli()
res, err := repo.DB().Exec(`
if err := r.DB().Exec(`
INSERT INTO tunnel(name, traffic_ratio, type, protocol, flow, created_time, updated_time, status, in_ip, inx)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, "legacy-null-chain", 1.0, 1, "tls", 1000, now, now, 1, nil, 1)
if err != nil {
`, "legacy-null-chain", 1.0, 1, "tls", 1000, now, now, 1, nil, 1).Error; err != nil {
t.Fatalf("seed tunnel for nullable chain export: %v", err)
}
tunnelID, err := res.LastInsertId()
if err != nil {
var tunnelID int64
if err := r.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&tunnelID); err != nil {
t.Fatalf("read tunnel id for nullable chain export: %v", err)
}
if _, err := repo.DB().Exec(`
if err := r.DB().Exec(`
INSERT INTO chain_tunnel(tunnel_id, chain_type, node_id, port, strategy, inx, protocol)
VALUES(?, ?, ?, ?, ?, ?, ?)
`, tunnelID, "1", 1, nil, nil, nil, nil); err != nil {
`, tunnelID, "1", 1, nil, nil, nil, nil).Error; err != nil {
t.Fatalf("seed nullable chain_tunnel row: %v", err)
}
@@ -596,19 +590,19 @@ func exportBackupPayload(t *testing.T, router http.Handler, path, token string)
return payload
}
func setupContractRouter(t *testing.T, jwtSecret string) (http.Handler, *sqlite.Repository) {
func setupContractRouter(t *testing.T, jwtSecret string) (http.Handler, *repo.Repository) {
t.Helper()
dbPath := filepath.Join(t.TempDir(), "contract.db")
repo, err := sqlite.Open(dbPath)
r, err := repo.Open(dbPath)
if err != nil {
t.Fatalf("open sqlite: %v", err)
}
t.Cleanup(func() {
_ = repo.Close()
_ = r.Close()
})
h := handler.New(repo, jwtSecret)
return httpserver.NewRouter(h, jwtSecret), repo
h := handler.New(r, jwtSecret)
return httpserver.NewRouter(h, jwtSecret), r
}
func TestOpenMigratesLegacyNodeDualStackColumns(t *testing.T) {
@@ -669,15 +663,15 @@ func TestOpenMigratesLegacyNodeDualStackColumns(t *testing.T) {
t.Fatalf("seed legacy node row: %v", err)
}
repo, err := sqlite.Open(dbPath)
r, err := repo.Open(dbPath)
if err != nil {
t.Fatalf("open migrated sqlite: %v", err)
}
t.Cleanup(func() {
_ = repo.Close()
_ = r.Close()
})
nodes, err := repo.ListNodes()
nodes, err := r.ListNodes()
if err != nil {
t.Fatalf("list nodes after migration: %v", err)
}
@@ -685,7 +679,7 @@ func TestOpenMigratesLegacyNodeDualStackColumns(t *testing.T) {
t.Fatalf("expected 1 node after migration, got %d", len(nodes))
}
columns := readTableColumns(t, repo.DB(), "node")
columns := readTableColumns(t, r.DB(), "node")
for _, required := range []string{"server_ip_v4", "server_ip_v6", "inx"} {
if !columns[required] {
@@ -693,16 +687,16 @@ func TestOpenMigratesLegacyNodeDualStackColumns(t *testing.T) {
}
}
tunnelColumns := readTableColumns(t, repo.DB(), "tunnel")
tunnelColumns := readTableColumns(t, r.DB(), "tunnel")
if !tunnelColumns["inx"] {
t.Fatalf("expected tunnel column %q to exist after migration", "inx")
}
}
func readTableColumns(t *testing.T, db *store.DB, table string) map[string]bool {
func readTableColumns(t *testing.T, db *gorm.DB, table string) map[string]bool {
t.Helper()
rows, err := db.Query("PRAGMA table_info(" + table + ")")
rows, err := db.Raw("PRAGMA table_info(" + table + ")").Rows()
if err != nil {
t.Fatalf("inspect %s columns: %v", table, err)
}
@@ -16,7 +16,7 @@ import (
"go-backend/internal/auth"
httpserver "go-backend/internal/http"
"go-backend/internal/http/handler"
"go-backend/internal/store/sqlite"
"go-backend/internal/store/repo"
)
func TestPostgresNodeCreateRepairsMissingIDDefaultContract(t *testing.T) {
@@ -44,35 +44,35 @@ func TestPostgresNodeCreateRepairsMissingIDDefaultContract(t *testing.T) {
t.Fatalf("build schema dsn: %v", err)
}
repo, err := sqlite.OpenPostgres(testDSN)
r, err := repo.OpenPostgres(testDSN)
if err != nil {
t.Fatalf("open postgres repository: %v", err)
}
if _, err := repo.DB().Exec(`ALTER TABLE node ALTER COLUMN id DROP DEFAULT`); err != nil {
_ = repo.Close()
if err := r.DB().Exec(`ALTER TABLE node ALTER COLUMN id DROP DEFAULT`).Error; err != nil {
_ = r.Close()
t.Fatalf("drop node.id default to simulate drift: %v", err)
}
if err := repo.Close(); err != nil {
if err := r.Close(); err != nil {
t.Fatalf("close repository before reopen: %v", err)
}
repo, err = sqlite.OpenPostgres(testDSN)
r, err = repo.OpenPostgres(testDSN)
if err != nil {
t.Fatalf("reopen postgres repository: %v", err)
}
t.Cleanup(func() {
_ = repo.Close()
_ = r.Close()
})
var columnDefault sql.NullString
if err := repo.DB().QueryRow(`
if err := r.DB().Raw(`
SELECT column_default
FROM information_schema.columns
WHERE table_schema = current_schema()
AND table_name = 'node'
AND column_name = 'id'
LIMIT 1
`).Scan(&columnDefault); err != nil {
`).Row().Scan(&columnDefault); err != nil {
t.Fatalf("query node.id default: %v", err)
}
if !columnDefault.Valid || !strings.Contains(strings.ToLower(columnDefault.String), "nextval(") {
@@ -80,7 +80,7 @@ func TestPostgresNodeCreateRepairsMissingIDDefaultContract(t *testing.T) {
}
jwtSecret := "postgres-contract-secret"
router := httpserver.NewRouter(handler.New(repo, jwtSecret), jwtSecret)
router := httpserver.NewRouter(handler.New(r, jwtSecret), jwtSecret)
token, err := auth.GenerateToken(1, "admin_user", 0, jwtSecret)
if err != nil {
t.Fatalf("generate admin token: %v", err)
@@ -95,7 +95,7 @@ func TestPostgresNodeCreateRepairsMissingIDDefaultContract(t *testing.T) {
assertCode(t, resp, 0)
var nodeID int64
if err := repo.DB().QueryRow(`SELECT id FROM node WHERE name = ? ORDER BY id DESC LIMIT 1`, "pg-repair-node").Scan(&nodeID); err != nil {
if err := r.DB().Raw(`SELECT id FROM node WHERE name = ? ORDER BY id DESC LIMIT 1`, "pg-repair-node").Row().Scan(&nodeID); err != nil {
t.Fatalf("query created node: %v", err)
}
if nodeID <= 0 {
@@ -26,15 +26,14 @@ func TestTunnelCreateRuntimeRollbackContract(t *testing.T) {
}
insertNode := func(name, ip, portRange string) int64 {
res, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO node(name, secret, server_ip, server_ip_v4, server_ip_v6, port, interface_name, version, http, tls, socks, created_time, updated_time, status, tcp_listen_addr, udp_listen_addr, inx)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, name, name+"-secret", ip, ip, "", portRange, "", "v1", 1, 1, 1, now, now, 1, "[::]", "[::]", 0)
if err != nil {
`, name, name+"-secret", ip, ip, "", portRange, "", "v1", 1, 1, 1, now, now, 1, "[::]", "[::]", 0).Error; err != nil {
t.Fatalf("insert node %s: %v", name, err)
}
id, err := res.LastInsertId()
if err != nil {
var id int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&id); err != nil {
t.Fatalf("get node id %s: %v", name, err)
}
return id
@@ -64,7 +63,7 @@ func TestTunnelCreateRuntimeRollbackContract(t *testing.T) {
}
var tunnelCount int
if err := repo.DB().QueryRow(`SELECT COUNT(1) FROM tunnel WHERE name = ?`, "runtime-rollback-tunnel").Scan(&tunnelCount); err != nil {
if err := repo.DB().Raw(`SELECT COUNT(1) FROM tunnel WHERE name = ?`, "runtime-rollback-tunnel").Row().Scan(&tunnelCount); err != nil {
t.Fatalf("count tunnel: %v", err)
}
if tunnelCount != 0 {
@@ -72,7 +71,7 @@ func TestTunnelCreateRuntimeRollbackContract(t *testing.T) {
}
var chainCount int
if err := repo.DB().QueryRow(`SELECT COUNT(1) FROM chain_tunnel`).Scan(&chainCount); err != nil {
if err := repo.DB().Raw(`SELECT COUNT(1) FROM chain_tunnel`).Row().Scan(&chainCount); err != nil {
t.Fatalf("count chain_tunnel: %v", err)
}
if chainCount != 0 {
@@ -91,15 +90,14 @@ func TestTunnelUpdateAssignsChainPortsContract(t *testing.T) {
}
insertNode := func(name, ip, portRange string) int64 {
res, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO node(name, secret, server_ip, server_ip_v4, server_ip_v6, port, interface_name, version, http, tls, socks, created_time, updated_time, status, tcp_listen_addr, udp_listen_addr, inx)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, name, name+"-secret", ip, ip, "", portRange, "", "v1", 1, 1, 1, now, now, 1, "[::]", "[::]", 0)
if err != nil {
`, name, name+"-secret", ip, ip, "", portRange, "", "v1", 1, 1, 1, now, now, 1, "[::]", "[::]", 0).Error; err != nil {
t.Fatalf("insert node %s: %v", name, err)
}
id, err := res.LastInsertId()
if err != nil {
var id int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&id); err != nil {
t.Fatalf("get node id %s: %v", name, err)
}
return id
@@ -109,15 +107,14 @@ func TestTunnelUpdateAssignsChainPortsContract(t *testing.T) {
chainID := insertNode("update-chain", "10.30.0.2", "41000-41010")
exitID := insertNode("update-exit", "10.30.0.3", "42000-42010")
tunnelRes, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO tunnel(name, traffic_ratio, type, protocol, flow, created_time, updated_time, status, in_ip, inx)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, "update-port-tunnel", 1.0, 1, "tls", 99999, now, now, 1, nil, 0)
if err != nil {
`, "update-port-tunnel", 1.0, 1, "tls", 99999, now, now, 1, nil, 0).Error; err != nil {
t.Fatalf("insert tunnel: %v", err)
}
tunnelID, err := tunnelRes.LastInsertId()
if err != nil {
var tunnelID int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&tunnelID); err != nil {
t.Fatalf("get tunnel id: %v", err)
}
@@ -131,7 +128,7 @@ func TestTunnelUpdateAssignsChainPortsContract(t *testing.T) {
assertCode(t, res, 0)
var chainPort int
if err := repo.DB().QueryRow(`SELECT port FROM chain_tunnel WHERE tunnel_id = ? AND chain_type = 2 LIMIT 1`, tunnelID).Scan(&chainPort); err != nil {
if err := repo.DB().Raw(`SELECT port FROM chain_tunnel WHERE tunnel_id = ? AND chain_type = 2 LIMIT 1`, tunnelID).Row().Scan(&chainPort); err != nil {
t.Fatalf("query chain port: %v", err)
}
if chainPort <= 0 {
@@ -139,7 +136,7 @@ func TestTunnelUpdateAssignsChainPortsContract(t *testing.T) {
}
var outPort int
if err := repo.DB().QueryRow(`SELECT port FROM chain_tunnel WHERE tunnel_id = ? AND chain_type = 3 LIMIT 1`, tunnelID).Scan(&outPort); err != nil {
if err := repo.DB().Raw(`SELECT port FROM chain_tunnel WHERE tunnel_id = ? AND chain_type = 3 LIMIT 1`, tunnelID).Row().Scan(&outPort); err != nil {
t.Fatalf("query out port: %v", err)
}
if outPort <= 0 {
@@ -147,7 +144,7 @@ func TestTunnelUpdateAssignsChainPortsContract(t *testing.T) {
}
var entryStrategy sql.NullString
if err := repo.DB().QueryRow(`SELECT strategy FROM chain_tunnel WHERE tunnel_id = ? AND chain_type = 1 LIMIT 1`, tunnelID).Scan(&entryStrategy); err != nil {
if err := repo.DB().Raw(`SELECT strategy FROM chain_tunnel WHERE tunnel_id = ? AND chain_type = 1 LIMIT 1`, tunnelID).Row().Scan(&entryStrategy); err != nil {
t.Fatalf("query entry strategy: %v", err)
}
if !entryStrategy.Valid || strings.TrimSpace(entryStrategy.String) == "" {
@@ -24,15 +24,14 @@ func TestTunnelCreateWithIPPreferenceContract(t *testing.T) {
}
insertDualStackNode := func(name, v4, v6, portRange string) int64 {
res, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO node(name, secret, server_ip, server_ip_v4, server_ip_v6, port, interface_name, version, http, tls, socks, created_time, updated_time, status, tcp_listen_addr, udp_listen_addr, inx)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, name, name+"-secret", v4, v4, v6, portRange, "", "v1", 1, 1, 1, now, now, 1, "[::]", "[::]", 0)
if err != nil {
`, name, name+"-secret", v4, v4, v6, portRange, "", "v1", 1, 1, 1, now, now, 1, "[::]", "[::]", 0).Error; err != nil {
t.Fatalf("insert node %s: %v", name, err)
}
id, err := res.LastInsertId()
if err != nil {
var id int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&id); err != nil {
t.Fatalf("get node id %s: %v", name, err)
}
return id
@@ -63,7 +62,7 @@ func TestTunnelCreateWithIPPreferenceContract(t *testing.T) {
}
var stored string
err := repo.DB().QueryRow(`SELECT COALESCE(ip_preference, '') FROM tunnel WHERE name = ?`, "tunnel-"+tc.name).Scan(&stored)
err := repo.DB().Raw(`SELECT COALESCE(ip_preference, '') FROM tunnel WHERE name = ?`, "tunnel-"+tc.name).Row().Scan(&stored)
if err != nil {
if err == sql.ErrNoRows {
t.Skipf("tunnel not created (nodes offline), skipping DB verification")
@@ -88,15 +87,14 @@ func TestTunnelUpdateIPPreferenceContract(t *testing.T) {
}
insertDualStackNode := func(name, v4, v6, portRange string) int64 {
res, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO node(name, secret, server_ip, server_ip_v4, server_ip_v6, port, interface_name, version, http, tls, socks, created_time, updated_time, status, tcp_listen_addr, udp_listen_addr, inx)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, name, name+"-secret", v4, v4, v6, portRange, "", "v1", 1, 1, 1, now, now, 1, "[::]", "[::]", 0)
if err != nil {
`, name, name+"-secret", v4, v4, v6, portRange, "", "v1", 1, 1, 1, now, now, 1, "[::]", "[::]", 0).Error; err != nil {
t.Fatalf("insert node %s: %v", name, err)
}
id, err := res.LastInsertId()
if err != nil {
var id int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&id); err != nil {
t.Fatalf("get node id %s: %v", name, err)
}
return id
@@ -105,15 +103,14 @@ func TestTunnelUpdateIPPreferenceContract(t *testing.T) {
entryID := insertDualStackNode("upd-entry", "10.60.0.1", "2001:db8:1::1", "60000-60010")
exitID := insertDualStackNode("upd-exit", "10.60.0.2", "2001:db8:1::2", "61000-61010")
tunnelRes, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO tunnel(name, traffic_ratio, type, protocol, flow, created_time, updated_time, status, in_ip, inx, ip_preference)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, "update-ip-pref-tunnel", 1.0, 1, "tls", 99999, now, now, 1, nil, 0, "")
if err != nil {
`, "update-ip-pref-tunnel", 1.0, 1, "tls", 99999, now, now, 1, nil, 0, "").Error; err != nil {
t.Fatalf("insert tunnel: %v", err)
}
tunnelID, err := tunnelRes.LastInsertId()
if err != nil {
var tunnelID int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&tunnelID); err != nil {
t.Fatalf("get tunnel id: %v", err)
}
@@ -130,7 +127,7 @@ func TestTunnelUpdateIPPreferenceContract(t *testing.T) {
}
var stored string
if err := repo.DB().QueryRow(`SELECT COALESCE(ip_preference, '') FROM tunnel WHERE id = ?`, tunnelID).Scan(&stored); err != nil {
if err := repo.DB().Raw(`SELECT COALESCE(ip_preference, '') FROM tunnel WHERE id = ?`, tunnelID).Row().Scan(&stored); err != nil {
t.Fatalf("query ip_preference: %v", err)
}
if stored != "v6" {
@@ -148,10 +145,10 @@ func TestTunnelListReturnsIPPreferenceContract(t *testing.T) {
t.Fatalf("generate admin token: %v", err)
}
_, err = repo.DB().Exec(`
err = repo.DB().Exec(`
INSERT INTO tunnel(name, traffic_ratio, type, protocol, flow, created_time, updated_time, status, in_ip, inx, ip_preference)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, "list-ip-pref-tunnel", 1.0, 2, "tls", 99999, now, now, 1, nil, 0, "v6")
`, "list-ip-pref-tunnel", 1.0, 2, "tls", 99999, now, now, 1, nil, 0, "v6").Error
if err != nil {
t.Fatalf("insert tunnel: %v", err)
}
@@ -198,16 +195,15 @@ func TestIPPreferenceColumnDefaultContract(t *testing.T) {
_, repo := setupContractRouter(t, "contract-jwt-secret")
now := time.Now().UnixMilli()
_, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO tunnel(name, traffic_ratio, type, protocol, flow, created_time, updated_time, status, in_ip, inx)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, "no-pref-tunnel", 1.0, 2, "tls", 99999, now, now, 1, nil, 0)
if err != nil {
`, "no-pref-tunnel", 1.0, 2, "tls", 99999, now, now, 1, nil, 0).Error; err != nil {
t.Fatalf("insert tunnel without ip_preference: %v", err)
}
var stored string
if err := repo.DB().QueryRow(`SELECT COALESCE(ip_preference, '') FROM tunnel WHERE name = ?`, "no-pref-tunnel").Scan(&stored); err != nil {
if err := repo.DB().Raw(`SELECT COALESCE(ip_preference, '') FROM tunnel WHERE name = ?`, "no-pref-tunnel").Row().Scan(&stored); err != nil {
t.Fatalf("query ip_preference: %v", err)
}
if stored != "" {
@@ -219,7 +215,7 @@ func TestIPPreferenceColumnMigrationContract(t *testing.T) {
_, repo := setupContractRouter(t, "contract-jwt-secret")
var colCount int
err := repo.DB().QueryRow(`SELECT COUNT(*) FROM pragma_table_info('tunnel') WHERE name = 'ip_preference'`).Scan(&colCount)
err := repo.DB().Raw(`SELECT COUNT(*) FROM pragma_table_info('tunnel') WHERE name = 'ip_preference'`).Row().Scan(&colCount)
if err != nil {
t.Fatalf("check column existence: %v", err)
}
@@ -232,16 +228,15 @@ func TestIPPreferenceCoalesceNullSafety(t *testing.T) {
_, repo := setupContractRouter(t, "contract-jwt-secret")
now := time.Now().UnixMilli()
_, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO tunnel(name, traffic_ratio, type, protocol, flow, created_time, updated_time, status, in_ip, inx, ip_preference)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)
`, "null-pref-tunnel", 1.0, 2, "tls", 99999, now, now, 1, nil, 0)
if err != nil {
`, "null-pref-tunnel", 1.0, 2, "tls", 99999, now, now, 1, nil, 0).Error; err != nil {
t.Skipf("DB does not allow NULL ip_preference (NOT NULL constraint): %v", err)
}
var stored string
if err := repo.DB().QueryRow(`SELECT COALESCE(ip_preference, '') FROM tunnel WHERE name = ?`, "null-pref-tunnel").Scan(&stored); err != nil {
if err := repo.DB().Raw(`SELECT COALESCE(ip_preference, '') FROM tunnel WHERE name = ?`, "null-pref-tunnel").Row().Scan(&stored); err != nil {
t.Fatalf("query ip_preference: %v", err)
}
if stored != "" {
@@ -253,16 +248,15 @@ func TestDualStackNodeIPFieldsStoredContract(t *testing.T) {
_, repo := setupContractRouter(t, "contract-jwt-secret")
now := time.Now().UnixMilli()
_, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO node(name, secret, server_ip, server_ip_v4, server_ip_v6, port, interface_name, version, http, tls, socks, created_time, updated_time, status, tcp_listen_addr, udp_listen_addr, inx)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, "ds-verify-node", "ds-secret", "10.70.0.1", "10.70.0.1", "2001:db8:2::1", "70000-70010", "", "v1", 1, 1, 1, now, now, 1, "[::]", "[::]", 0)
if err != nil {
`, "ds-verify-node", "ds-secret", "10.70.0.1", "10.70.0.1", "2001:db8:2::1", "70000-70010", "", "v1", 1, 1, 1, now, now, 1, "[::]", "[::]", 0).Error; err != nil {
t.Fatalf("insert dual-stack node: %v", err)
}
var v4, v6 sql.NullString
if err := repo.DB().QueryRow(`SELECT server_ip_v4, server_ip_v6 FROM node WHERE name = ?`, "ds-verify-node").Scan(&v4, &v6); err != nil {
if err := repo.DB().Raw(`SELECT server_ip_v4, server_ip_v6 FROM node WHERE name = ?`, "ds-verify-node").Row().Scan(&v4, &v6); err != nil {
t.Fatalf("query node IPs: %v", err)
}
if !v4.Valid || v4.String != "10.70.0.1" {
@@ -282,16 +276,15 @@ func TestIPPreferenceValidValuesContract(t *testing.T) {
if pref == "" {
name = "valid-pref-empty"
}
_, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO tunnel(name, traffic_ratio, type, protocol, flow, created_time, updated_time, status, in_ip, inx, ip_preference)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, name, 1.0, 2, "tls", 99999, now, now, 1, nil, 0, pref)
if err != nil {
`, name, 1.0, 2, "tls", 99999, now, now, 1, nil, 0, pref).Error; err != nil {
t.Fatalf("insert tunnel with ip_preference=%q: %v", pref, err)
}
var stored string
if err := repo.DB().QueryRow(`SELECT COALESCE(ip_preference, '') FROM tunnel WHERE name = ?`, name).Scan(&stored); err != nil {
if err := repo.DB().Raw(`SELECT COALESCE(ip_preference, '') FROM tunnel WHERE name = ?`, name).Row().Scan(&stored); err != nil {
t.Fatalf("query ip_preference for %s: %v", name, err)
}
if stored != pref {
@@ -16,23 +16,22 @@ func TestUserTunnelVisibleListContracts(t *testing.T) {
router, repo := setupDiagnosisContractRouter(t, secret)
now := time.Now().UnixMilli()
if _, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO user(id, user, pwd, role_id, exp_time, flow, in_flow, out_flow, flow_reset_time, num, created_time, updated_time, status)
VALUES(2, 'normal_user', '3c85cdebade1c51cf64ca9f3c09d182d', 1, 2727251700000, 99999, 0, 0, 1, 99999, ?, ?, 1)
`, now, now); err != nil {
`, now, now).Error; err != nil {
t.Fatalf("insert user: %v", err)
}
insertTunnel := func(name string, status int, inx int64) int64 {
res, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO tunnel(name, traffic_ratio, type, protocol, flow, created_time, updated_time, status, in_ip, inx)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, name, 1.0, 1, "tls", 99999, now, now, status, nil, inx)
if err != nil {
`, name, 1.0, 1, "tls", 99999, now, now, status, nil, inx).Error; err != nil {
t.Fatalf("insert tunnel %s: %v", name, err)
}
id, err := res.LastInsertId()
if err != nil {
var id int64
if err := repo.DB().Raw("SELECT last_insert_rowid()").Row().Scan(&id); err != nil {
t.Fatalf("get tunnel id %s: %v", name, err)
}
return id
@@ -42,22 +41,22 @@ func TestUserTunnelVisibleListContracts(t *testing.T) {
enabledB := insertTunnel("enabled-B", 1, 2)
disabledC := insertTunnel("disabled-C", 0, 3)
if _, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO user_tunnel(user_id, tunnel_id, speed_id, num, flow, in_flow, out_flow, flow_reset_time, exp_time, status)
VALUES(?, ?, NULL, ?, ?, 0, 0, ?, ?, ?)
`, 2, enabledA, 100, 1000, 1, 2727251700000, 0); err != nil {
`, 2, enabledA, 100, 1000, 1, 2727251700000, 0).Error; err != nil {
t.Fatalf("insert user_tunnel enabledA: %v", err)
}
if _, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO user_tunnel(user_id, tunnel_id, speed_id, num, flow, in_flow, out_flow, flow_reset_time, exp_time, status)
VALUES(?, ?, NULL, ?, ?, 0, 0, ?, ?, ?)
`, 2, enabledB, 100, 1000, 1, 2727251700000, 1); err != nil {
`, 2, enabledB, 100, 1000, 1, 2727251700000, 1).Error; err != nil {
t.Fatalf("insert user_tunnel enabledB: %v", err)
}
if _, err := repo.DB().Exec(`
if err := repo.DB().Exec(`
INSERT INTO user_tunnel(user_id, tunnel_id, speed_id, num, flow, in_flow, out_flow, flow_reset_time, exp_time, status)
VALUES(?, ?, NULL, ?, ?, 0, 0, ?, ?, ?)
`, 2, disabledC, 100, 1000, 1, 2727251700000, 1); err != nil {
`, 2, disabledC, 100, 1000, 1, 2727251700000, 1).Error; err != nil {
t.Fatalf("insert user_tunnel disabledC: %v", err)
}