Skip to content

Commit cab62df

Browse files
committed
Add integration test for forks
1 parent 58fa1b3 commit cab62df

File tree

1 file changed

+365
-0
lines changed

1 file changed

+365
-0
lines changed

internal/tiger/cmd/integration_test.go

Lines changed: 365 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,3 +1307,368 @@ func TestAuthenticationErrorsIntegration(t *testing.T) {
13071307
})
13081308
}
13091309
}
1310+
1311+
// TestServiceForkIntegration tests forking a service with --now strategy and validates data is correctly copied
1312+
func TestServiceForkIntegration(t *testing.T) {
1313+
config.SetTestServiceName(t)
1314+
// Check for required environment variables
1315+
publicKey := os.Getenv("TIGER_PUBLIC_KEY_INTEGRATION")
1316+
secretKey := os.Getenv("TIGER_SECRET_KEY_INTEGRATION")
1317+
projectID := os.Getenv("TIGER_PROJECT_ID_INTEGRATION")
1318+
if publicKey == "" || secretKey == "" || projectID == "" {
1319+
t.Skip("Skipping integration test: TIGER_PUBLIC_KEY_INTEGRATION, TIGER_SECRET_KEY_INTEGRATION, and TIGER_PROJECT_ID_INTEGRATION must be set")
1320+
}
1321+
1322+
// Set up isolated test environment with temporary config directory
1323+
tmpDir := setupIntegrationTest(t)
1324+
t.Logf("Using temporary config directory: %s", tmpDir)
1325+
1326+
// Generate unique names to avoid conflicts
1327+
timestamp := time.Now().Unix()
1328+
sourceServiceName := fmt.Sprintf("integration-fork-source-%d", timestamp)
1329+
tableName := fmt.Sprintf("fork_test_data_%d", timestamp)
1330+
1331+
var sourceServiceID string
1332+
var forkedServiceID string
1333+
1334+
// Always logout at the end to clean up credentials
1335+
defer func() {
1336+
t.Logf("Cleaning up authentication")
1337+
_, err := executeIntegrationCommand(t.Context(), "auth", "logout")
1338+
if err != nil {
1339+
t.Logf("Warning: Failed to logout: %v", err)
1340+
}
1341+
}()
1342+
1343+
// Cleanup function to ensure source service is deleted
1344+
defer func() {
1345+
if sourceServiceID != "" {
1346+
t.Logf("Cleaning up source service: %s", sourceServiceID)
1347+
_, err := executeIntegrationCommand(
1348+
t.Context(),
1349+
"service", "delete", sourceServiceID,
1350+
"--confirm",
1351+
"--wait-timeout", "5m",
1352+
)
1353+
if err != nil {
1354+
t.Logf("Warning: Failed to cleanup source service %s: %v", sourceServiceID, err)
1355+
}
1356+
}
1357+
}()
1358+
1359+
// Cleanup function to ensure forked service is deleted
1360+
defer func() {
1361+
if forkedServiceID != "" {
1362+
t.Logf("Cleaning up forked service: %s", forkedServiceID)
1363+
_, err := executeIntegrationCommand(
1364+
t.Context(),
1365+
"service", "delete", forkedServiceID,
1366+
"--confirm",
1367+
"--wait-timeout", "5m",
1368+
)
1369+
if err != nil {
1370+
t.Logf("Warning: Failed to cleanup forked service %s: %v", forkedServiceID, err)
1371+
}
1372+
}
1373+
}()
1374+
1375+
t.Run("Login", func(t *testing.T) {
1376+
t.Logf("Logging in with public key: %s", publicKey[:8]+"...") // Only show first 8 chars
1377+
1378+
output, err := executeIntegrationCommand(
1379+
t.Context(),
1380+
"auth", "login",
1381+
"--public-key", publicKey,
1382+
"--secret-key", secretKey,
1383+
"--project-id", projectID,
1384+
)
1385+
1386+
if err != nil {
1387+
t.Fatalf("Login failed: %v\nOutput: %s", err, output)
1388+
}
1389+
1390+
t.Logf("Login successful")
1391+
})
1392+
1393+
t.Run("CreateSourceService", func(t *testing.T) {
1394+
t.Logf("Creating source service: %s", sourceServiceName)
1395+
1396+
output, err := executeIntegrationCommand(
1397+
t.Context(),
1398+
"service", "create",
1399+
"--name", sourceServiceName,
1400+
"--cpu", "shared",
1401+
"--wait-timeout", "15m",
1402+
"--no-set-default",
1403+
"--output", "json",
1404+
)
1405+
1406+
if err != nil {
1407+
t.Fatalf("Source service creation failed: %v\nOutput: %s", err, output)
1408+
}
1409+
1410+
extractedServiceID := extractServiceIDFromCreateOutput(t, output)
1411+
if extractedServiceID == "" {
1412+
t.Fatalf("Could not extract source service ID from create output: %s", output)
1413+
}
1414+
1415+
sourceServiceID = extractedServiceID
1416+
t.Logf("Created source service with ID: %s", sourceServiceID)
1417+
})
1418+
1419+
t.Run("InsertTestData", func(t *testing.T) {
1420+
if sourceServiceID == "" {
1421+
t.Skip("No source service ID available")
1422+
}
1423+
1424+
t.Logf("Creating test table: %s", tableName)
1425+
1426+
// Create table
1427+
output, err := executeIntegrationCommand(
1428+
t.Context(),
1429+
"db", "psql", sourceServiceID,
1430+
"--", "-c", fmt.Sprintf("CREATE TABLE %s (id INT PRIMARY KEY, data TEXT, created_at TIMESTAMP DEFAULT NOW());", tableName),
1431+
)
1432+
1433+
if err != nil {
1434+
t.Fatalf("Failed to create test table: %v\nOutput: %s", err, output)
1435+
}
1436+
1437+
t.Logf("Inserting test data into table: %s", tableName)
1438+
1439+
// Insert test data
1440+
output, err = executeIntegrationCommand(
1441+
t.Context(),
1442+
"db", "psql", sourceServiceID,
1443+
"--", "-c", fmt.Sprintf("INSERT INTO %s (id, data) VALUES (1, 'test-row-1'), (2, 'test-row-2'), (3, 'test-row-3');", tableName),
1444+
)
1445+
1446+
if err != nil {
1447+
t.Fatalf("Failed to insert test data: %v\nOutput: %s", err, output)
1448+
}
1449+
1450+
t.Logf("✅ Test data inserted successfully")
1451+
})
1452+
1453+
t.Run("VerifySourceData", func(t *testing.T) {
1454+
if sourceServiceID == "" {
1455+
t.Skip("No source service ID available")
1456+
}
1457+
1458+
t.Logf("Verifying test data in source service")
1459+
1460+
output, err := executeIntegrationCommand(
1461+
t.Context(),
1462+
"db", "psql", sourceServiceID,
1463+
"--", "-c", fmt.Sprintf("SELECT * FROM %s ORDER BY id;", tableName),
1464+
)
1465+
1466+
if err != nil {
1467+
t.Fatalf("Failed to query test data: %v\nOutput: %s", err, output)
1468+
}
1469+
1470+
// Verify all three rows are present
1471+
if !strings.Contains(output, "test-row-1") {
1472+
t.Errorf("Expected 'test-row-1' in output, got: %s", output)
1473+
}
1474+
if !strings.Contains(output, "test-row-2") {
1475+
t.Errorf("Expected 'test-row-2' in output, got: %s", output)
1476+
}
1477+
if !strings.Contains(output, "test-row-3") {
1478+
t.Errorf("Expected 'test-row-3' in output, got: %s", output)
1479+
}
1480+
1481+
t.Logf("✅ Source data verified: 3 rows present")
1482+
})
1483+
1484+
t.Run("ForkService", func(t *testing.T) {
1485+
if sourceServiceID == "" {
1486+
t.Skip("No source service ID available")
1487+
}
1488+
1489+
t.Logf("Forking service: %s with --now strategy", sourceServiceID)
1490+
1491+
output, err := executeIntegrationCommand(
1492+
t.Context(),
1493+
"service", "fork", sourceServiceID,
1494+
"--now",
1495+
"--wait-timeout", "15m",
1496+
"--no-set-default",
1497+
"--output", "json",
1498+
)
1499+
1500+
if err != nil {
1501+
t.Fatalf("Service fork failed: %v\nOutput: %s", err, output)
1502+
}
1503+
1504+
extractedServiceID := extractServiceIDFromCreateOutput(t, output)
1505+
if extractedServiceID == "" {
1506+
t.Fatalf("Could not extract forked service ID from fork output: %s", output)
1507+
}
1508+
1509+
forkedServiceID = extractedServiceID
1510+
t.Logf("✅ Created forked service with ID: %s", forkedServiceID)
1511+
})
1512+
1513+
t.Run("VerifyForkedData", func(t *testing.T) {
1514+
if forkedServiceID == "" {
1515+
t.Skip("No forked service ID available")
1516+
}
1517+
1518+
t.Logf("Verifying test data in forked service")
1519+
1520+
output, err := executeIntegrationCommand(
1521+
t.Context(),
1522+
"db", "psql", forkedServiceID,
1523+
"--", "-c", fmt.Sprintf("SELECT * FROM %s ORDER BY id;", tableName),
1524+
)
1525+
1526+
if err != nil {
1527+
t.Fatalf("Failed to query forked service data: %v\nOutput: %s", err, output)
1528+
}
1529+
1530+
// Verify all three rows are present in fork
1531+
if !strings.Contains(output, "test-row-1") {
1532+
t.Errorf("Expected 'test-row-1' in forked service output, got: %s", output)
1533+
}
1534+
if !strings.Contains(output, "test-row-2") {
1535+
t.Errorf("Expected 'test-row-2' in forked service output, got: %s", output)
1536+
}
1537+
if !strings.Contains(output, "test-row-3") {
1538+
t.Errorf("Expected 'test-row-3' in forked service output, got: %s", output)
1539+
}
1540+
1541+
t.Logf("✅ Forked data verified: 3 rows present matching source")
1542+
})
1543+
1544+
t.Run("VerifyDataIndependence", func(t *testing.T) {
1545+
if sourceServiceID == "" || forkedServiceID == "" {
1546+
t.Skip("Source or forked service ID not available")
1547+
}
1548+
1549+
t.Logf("Verifying data independence between source and fork")
1550+
1551+
// Insert new data in forked service
1552+
t.Logf("Inserting new row in forked service")
1553+
output, err := executeIntegrationCommand(
1554+
t.Context(),
1555+
"db", "psql", forkedServiceID,
1556+
"--", "-c", fmt.Sprintf("INSERT INTO %s (id, data) VALUES (4, 'fork-only-row');", tableName),
1557+
)
1558+
1559+
if err != nil {
1560+
t.Fatalf("Failed to insert data in fork: %v\nOutput: %s", err, output)
1561+
}
1562+
1563+
// Verify fork has 4 rows
1564+
t.Logf("Verifying fork has 4 rows")
1565+
output, err = executeIntegrationCommand(
1566+
t.Context(),
1567+
"db", "psql", forkedServiceID,
1568+
"--", "-c", fmt.Sprintf("SELECT COUNT(*) FROM %s;", tableName),
1569+
)
1570+
1571+
if err != nil {
1572+
t.Fatalf("Failed to count rows in fork: %v\nOutput: %s", err, output)
1573+
}
1574+
1575+
if !strings.Contains(output, "4") {
1576+
t.Errorf("Expected 4 rows in fork after insert, got: %s", output)
1577+
}
1578+
1579+
// Verify source still has 3 rows (unchanged)
1580+
t.Logf("Verifying source still has 3 rows (unchanged)")
1581+
output, err = executeIntegrationCommand(
1582+
t.Context(),
1583+
"db", "psql", sourceServiceID,
1584+
"--", "-c", fmt.Sprintf("SELECT COUNT(*) FROM %s;", tableName),
1585+
)
1586+
1587+
if err != nil {
1588+
t.Fatalf("Failed to count rows in source: %v\nOutput: %s", err, output)
1589+
}
1590+
1591+
if !strings.Contains(output, "3") {
1592+
t.Errorf("Expected 3 rows in source (unchanged), got: %s", output)
1593+
}
1594+
1595+
// Verify source doesn't have fork-only row
1596+
output, err = executeIntegrationCommand(
1597+
t.Context(),
1598+
"db", "psql", sourceServiceID,
1599+
"--", "-c", fmt.Sprintf("SELECT * FROM %s WHERE data = 'fork-only-row';", tableName),
1600+
)
1601+
1602+
if err != nil {
1603+
t.Fatalf("Failed to query source for fork-only row: %v\nOutput: %s", err, output)
1604+
}
1605+
1606+
// The output should show "0 rows" or similar since the row shouldn't exist
1607+
if strings.Contains(output, "fork-only-row") {
1608+
t.Errorf("Source service should not contain fork-only row, but got: %s", output)
1609+
}
1610+
1611+
t.Logf("✅ Data independence verified: fork and source are truly independent")
1612+
})
1613+
1614+
t.Run("DeleteSourceService", func(t *testing.T) {
1615+
if sourceServiceID == "" {
1616+
t.Skip("No source service ID available")
1617+
}
1618+
1619+
t.Logf("Deleting source service: %s", sourceServiceID)
1620+
1621+
output, err := executeIntegrationCommand(
1622+
t.Context(),
1623+
"service", "delete", sourceServiceID,
1624+
"--confirm",
1625+
"--wait-timeout", "10m",
1626+
)
1627+
1628+
if err != nil {
1629+
t.Fatalf("Source service deletion failed: %v\nOutput: %s", err, output)
1630+
}
1631+
1632+
// Clear sourceServiceID so cleanup doesn't try to delete again
1633+
sourceServiceID = ""
1634+
t.Logf("✅ Source service deleted successfully")
1635+
})
1636+
1637+
t.Run("DeleteForkedService", func(t *testing.T) {
1638+
if forkedServiceID == "" {
1639+
t.Skip("No forked service ID available")
1640+
}
1641+
1642+
t.Logf("Deleting forked service: %s", forkedServiceID)
1643+
1644+
output, err := executeIntegrationCommand(
1645+
t.Context(),
1646+
"service", "delete", forkedServiceID,
1647+
"--confirm",
1648+
"--wait-timeout", "10m",
1649+
)
1650+
1651+
if err != nil {
1652+
t.Fatalf("Forked service deletion failed: %v\nOutput: %s", err, output)
1653+
}
1654+
1655+
// Clear forkedServiceID so cleanup doesn't try to delete again
1656+
forkedServiceID = ""
1657+
t.Logf("✅ Forked service deleted successfully")
1658+
})
1659+
1660+
t.Run("Logout", func(t *testing.T) {
1661+
t.Logf("Logging out")
1662+
1663+
output, err := executeIntegrationCommand(
1664+
t.Context(),
1665+
"auth", "logout",
1666+
)
1667+
1668+
if err != nil {
1669+
t.Fatalf("Logout failed: %v\nOutput: %s", err, output)
1670+
}
1671+
1672+
t.Logf("Logout successful")
1673+
})
1674+
}

0 commit comments

Comments
 (0)