Skip to content

Commit 4a42991

Browse files
committed
chore: run all SyncAcceptanceTests against syncv2; add assertions on stats (#16814)
1 parent ec78f59 commit 4a42991

File tree

3 files changed

+354
-264
lines changed

3 files changed

+354
-264
lines changed

airbyte-test-utils/src/main/kotlin/io/airbyte/test/utils/AcceptanceTestHarness.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ import io.airbyte.commons.temporal.scheduling.state.WorkflowState
8585
import io.airbyte.db.Database
8686
import io.airbyte.db.factory.DataSourceFactory.close
8787
import io.airbyte.db.jdbc.JdbcUtils
88+
import io.airbyte.featureflag.Context
89+
import io.airbyte.featureflag.Flag
90+
import io.airbyte.featureflag.tests.TestFlagsSetter
91+
import io.airbyte.featureflag.tests.TestFlagsSetter.FlagOverride
8892
import io.airbyte.test.utils.AcceptanceTestHarness
8993
import io.airbyte.test.utils.Databases.createDataSource
9094
import io.airbyte.test.utils.Databases.createDslContext
@@ -145,6 +149,7 @@ class AcceptanceTestHarness
145149
@JvmField val apiClient: AirbyteApiClient,
146150
private val defaultWorkspaceId: UUID,
147151
private val postgresSqlInitFile: String? = DEFAULT_POSTGRES_INIT_SQL_FILE,
152+
val testFlagsSetter: TestFlagsSetter? = null,
148153
) {
149154
@JvmField
150155
val dataplaneGroupId: UUID
@@ -1346,6 +1351,16 @@ class AcceptanceTestHarness
13461351
Assertions.assertEquals(expectedAirbyteCatalog, actual)
13471352
}
13481353

1354+
// don't delete this even if it's unused!
1355+
// this is only useful for testing feature flags, so if we do a good job of cleaning up flags,
1356+
// this function should have no usages most of the time.
1357+
// but we should keep it around regardless, so that we can always test flags easily.
1358+
fun <T> withFlag(
1359+
flag: Flag<T>,
1360+
context: Context?,
1361+
value: T,
1362+
): FlagOverride<T> = testFlagsSetter!!.withFlag(flag, value, context)
1363+
13491364
/**
13501365
* Validates that job logs exist, are in the correct format and contain entries from various
13511366
* participants. This method loops because not all participants may have reported by the time that a

airbyte-tests/src/test-acceptance/kotlin/io/airbyte/test/acceptance/AcceptanceTestsResources.kt

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ import com.google.common.collect.Sets
1111
import dev.failsafe.Failsafe
1212
import dev.failsafe.RetryPolicy
1313
import dev.failsafe.function.CheckedRunnable
14+
import io.airbyte.api.client.model.generated.ConnectionEventsRequestBody
1415
import io.airbyte.api.client.model.generated.ConnectionScheduleData
1516
import io.airbyte.api.client.model.generated.ConnectionScheduleDataBasicSchedule
1617
import io.airbyte.api.client.model.generated.DestinationDefinitionIdRequestBody
1718
import io.airbyte.api.client.model.generated.DestinationSyncMode
19+
import io.airbyte.api.client.model.generated.JobIdRequestBody
1820
import io.airbyte.api.client.model.generated.JobRead
1921
import io.airbyte.api.client.model.generated.JobStatus
2022
import io.airbyte.api.client.model.generated.SourceDefinitionIdRequestBody
@@ -24,6 +26,7 @@ import io.airbyte.api.client.model.generated.SyncMode
2426
import io.airbyte.api.client.model.generated.WorkspaceCreate
2527
import io.airbyte.commons.DEFAULT_ORGANIZATION_ID
2628
import io.airbyte.commons.json.Jsons
29+
import io.airbyte.featureflag.tests.TestFlagsSetter
2730
import io.airbyte.test.utils.AcceptanceTestHarness
2831
import io.airbyte.test.utils.AcceptanceTestUtils
2932
import io.airbyte.test.utils.AcceptanceTestUtils.createAirbyteApiClient
@@ -131,6 +134,55 @@ class AcceptanceTestsResources {
131134

132135
LOGGER.info(STATE_AFTER_SYNC_ONE, testHarness.getConnectionState(connectionId))
133136

137+
// postgres_init.sql inserts 5 records. Assert that we wrote stats correctly.
138+
// (this is a bit sketchy, in that theoretically the source could emit a state message,
139+
// then fail an attempt, and a subsequent attempt would then not read all the records.
140+
// But with just 5 records, that seems unlikely.)
141+
val lastAttempt =
142+
testHarness
143+
.getJobInfoRead(connectionSyncRead1.job.id)
144+
.attempts
145+
.last()
146+
.attempt
147+
testHarness.apiClient.jobsApi.getJobDebugInfo(JobIdRequestBody(connectionSyncRead1.job.id))
148+
Assertions.assertAll(
149+
"totalStats were incorrect",
150+
{ Assertions.assertEquals(5, lastAttempt.totalStats!!.recordsEmitted, "totalStats.recordsEmitted was incorrect") },
151+
{ Assertions.assertEquals(118, lastAttempt.totalStats!!.bytesEmitted, "totalStats.bytesEmitted was incorrect") },
152+
{ Assertions.assertEquals(1, lastAttempt.totalStats!!.stateMessagesEmitted, "totalStats.stateMessagesEmitted was incorrect") },
153+
// the API doesn't return records/bytes committed on totalStats, so don't assert against them
154+
// { Assertions.assertEquals(5, lastAttempt.totalStats!!.recordsCommitted, "totalStats.recordsCommitted was incorrect") },
155+
// { Assertions.assertEquals(118, lastAttempt.totalStats!!.bytesCommitted, "totalStats.bytesCommitted was incorrect") },
156+
)
157+
Assertions.assertEquals(1, lastAttempt.streamStats!!.size, "Expected to see stats for exactly one stream. Got ${lastAttempt.streamStats}")
158+
val lastAttemptStreamStats = lastAttempt.streamStats!!.first()
159+
Assertions.assertAll(
160+
"streamStats were incorrect",
161+
{ Assertions.assertEquals("id_and_name", lastAttemptStreamStats.streamName) },
162+
{ Assertions.assertNull(lastAttemptStreamStats.streamNamespace) },
163+
)
164+
Assertions.assertAll(
165+
"streamStats were incorrect",
166+
{ Assertions.assertEquals(5, lastAttemptStreamStats.stats.recordsEmitted, "streamStats.recordsEmitted was incorrect") },
167+
{ Assertions.assertEquals(118, lastAttemptStreamStats.stats.bytesEmitted, "streamStats.bytesEmitted was incorrect") },
168+
{ Assertions.assertEquals(5, lastAttemptStreamStats.stats.recordsCommitted, "streamStats.recordsCommitted was incorrect") },
169+
// the API doesn't return stateMessagesEmitted / bytesCommitted on streamStats, so don't assert against them
170+
// { Assertions.assertEquals(1, lastAttemptStreamStats.stats.stateMessagesEmitted, "streamStats.stateMessagesEmitted was incorrect") },
171+
// { Assertions.assertEquals(118, lastAttemptStreamStats.stats.bytesCommitted, "streamStats.bytesCommitted was incorrect") },
172+
)
173+
// this was the only way I found to get to bytesLoaded (conceptually equivalent to bytesCommitted)
174+
val lastConnectionEventSummary =
175+
testHarness
176+
.apiClient
177+
.connectionApi
178+
.listConnectionEvents(ConnectionEventsRequestBody(connectionId))
179+
.events
180+
.first()
181+
// summary is declared as Any, so we need to explicitly cast here.
182+
.summary as Map<String, Int>
183+
// would you believe "bytesLoaded" isn't declared as a constant anywhere?
184+
Assertions.assertEquals(118, lastConnectionEventSummary["bytesLoaded"])
185+
134186
val src = testHarness.getSourceDatabase()
135187
val dst = testHarness.getDestinationDatabase()
136188
assertSourceAndDestinationDbRawRecordsInSync(
@@ -348,6 +400,7 @@ class AcceptanceTestsResources {
348400
AcceptanceTestUtils.getAirbyteApiUrl(),
349401
Map.of(GATEWAY_AUTH_HEADER, CLOUD_API_USER_HEADER_VALUE),
350402
)
403+
val testFlagsSetter = TestFlagsSetter(AIRBYTE_SERVER_HOST)
351404

352405
// If a workspace id is passed, use that. Otherwise, create a new workspace.
353406
// NOTE: we want to sometimes use a pre-configured workspace e.g., if we run against a production
@@ -393,7 +446,7 @@ class AcceptanceTestsResources {
393446
LOGGER.info("pg source definition: {}", sourceDef.dockerImageTag)
394447
LOGGER.info("pg destination definition: {}", destinationDef.dockerImageTag)
395448

396-
testHarness = AcceptanceTestHarness(apiClient = airbyteApiClient, defaultWorkspaceId = workspaceId)
449+
testHarness = AcceptanceTestHarness(apiClient = airbyteApiClient, defaultWorkspaceId = workspaceId, testFlagsSetter = testFlagsSetter)
397450

398451
testHarness.ensureCleanSlate()
399452
}
@@ -424,6 +477,7 @@ class AcceptanceTestsResources {
424477
// NOTE: this is just a base64 encoding of a jwt representing a test user in some deployments.
425478
const val CLOUD_API_USER_HEADER_VALUE: String = "eyJ1c2VyX2lkIjogImNsb3VkLWFwaSIsICJlbWFpbF92ZXJpZmllZCI6ICJ0cnVlIn0K"
426479
const val AIRBYTE_ACCEPTANCE_TEST_WORKSPACE_ID: String = "AIRBYTE_ACCEPTANCE_TEST_WORKSPACE_ID"
480+
val AIRBYTE_SERVER_HOST: String = Optional.ofNullable(System.getenv("AIRBYTE_SERVER_HOST")).orElse("http://localhost:8001")
427481
val POSTGRES_SOURCE_DEF_ID: UUID = UUID.fromString("decd338e-5647-4c0b-adf4-da0e75f5a750")
428482
val POSTGRES_DEST_DEF_ID: UUID = UUID.fromString("25c5221d-dce2-4163-ade9-739ef790f503")
429483
const val KUBE: String = "KUBE"

0 commit comments

Comments
 (0)