-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bulk Load CDK: Process Records Unit Tests; Test cleanup (#45846)
- Loading branch information
1 parent
8ecebea
commit 477bcc4
Showing
4 changed files
with
242 additions
and
100 deletions.
There are no files selected for viewing
70 changes: 70 additions & 0 deletions
70
airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/file/MockTempFileProvider.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/* | ||
* Copyright (c) 2024 Airbyte, Inc., all rights reserved. | ||
*/ | ||
|
||
package io.airbyte.cdk.file | ||
|
||
import io.micronaut.context.annotation.Requires | ||
import jakarta.inject.Singleton | ||
import java.nio.file.Path | ||
|
||
@Singleton | ||
@Requires(env = ["MockTempFileProvider"]) | ||
class MockTempFileProvider : TempFileProvider { | ||
class MockLocalFile : LocalFile { | ||
val writtenLines: MutableList<String> = mutableListOf() | ||
var linesToRead: MutableList<String> = mutableListOf() | ||
val writersCreated: MutableList<MockFileWriter> = mutableListOf() | ||
val readersCreated: MutableList<MockFileReader> = mutableListOf() | ||
var isDeleted: Boolean = false | ||
|
||
class MockFileWriter(val file: MockLocalFile) : FileWriter { | ||
var isClosed = false | ||
|
||
override fun write(str: String) { | ||
file.writtenLines.add(str) | ||
} | ||
|
||
override fun close() { | ||
isClosed = true | ||
} | ||
} | ||
|
||
class MockFileReader(val file: MockLocalFile) : FileReader { | ||
var isClosed = false | ||
var index = 0 | ||
override fun lines(): Sequence<String> { | ||
return sequence { | ||
while (index < file.linesToRead.size) { | ||
yield(file.linesToRead[index]) | ||
index++ | ||
} | ||
} | ||
} | ||
|
||
override fun close() { | ||
isClosed = true | ||
} | ||
} | ||
|
||
override fun toFileWriter(): FileWriter { | ||
val writer = MockFileWriter(this) | ||
writersCreated.add(writer) | ||
return writer | ||
} | ||
|
||
override fun toFileReader(): FileReader { | ||
val reader = MockFileReader(this) | ||
readersCreated.add(reader) | ||
return reader | ||
} | ||
|
||
override fun delete() { | ||
isDeleted = true | ||
} | ||
} | ||
|
||
override fun createTempFile(directory: Path, prefix: String, suffix: String): LocalFile { | ||
return MockLocalFile() | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/task/MockTaskLauncher.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
* Copyright (c) 2024 Airbyte, Inc., all rights reserved. | ||
*/ | ||
|
||
package io.airbyte.cdk.task | ||
|
||
import io.airbyte.cdk.command.DestinationStream | ||
import io.airbyte.cdk.message.BatchEnvelope | ||
import io.airbyte.cdk.message.SpilledRawMessagesLocalFile | ||
import io.airbyte.cdk.write.StreamLoader | ||
|
||
class MockTaskLauncher(override val taskRunner: TaskRunner) : DestinationTaskLauncher { | ||
val spilledFiles = mutableListOf<BatchEnvelope<SpilledRawMessagesLocalFile>>() | ||
val batchEnvelopes = mutableListOf<BatchEnvelope<*>>() | ||
|
||
override suspend fun handleSetupComplete() { | ||
throw NotImplementedError() | ||
} | ||
|
||
override suspend fun handleStreamOpen(streamLoader: StreamLoader) { | ||
throw NotImplementedError() | ||
} | ||
|
||
override suspend fun handleNewSpilledFile( | ||
stream: DestinationStream, | ||
wrapped: BatchEnvelope<SpilledRawMessagesLocalFile> | ||
) { | ||
spilledFiles.add(wrapped) | ||
} | ||
|
||
override suspend fun handleNewBatch(streamLoader: StreamLoader, wrapped: BatchEnvelope<*>) { | ||
batchEnvelopes.add(wrapped) | ||
} | ||
|
||
override suspend fun handleStreamClosed(stream: DestinationStream) { | ||
throw NotImplementedError() | ||
} | ||
|
||
override suspend fun handleTeardownComplete() { | ||
throw NotImplementedError() | ||
} | ||
|
||
override suspend fun start() { | ||
throw NotImplementedError() | ||
} | ||
} |
112 changes: 112 additions & 0 deletions
112
airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/task/ProcessRecordsTaskTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
/* | ||
* Copyright (c) 2024 Airbyte, Inc., all rights reserved. | ||
*/ | ||
|
||
package io.airbyte.cdk.task | ||
|
||
import com.google.common.collect.Range | ||
import io.airbyte.cdk.command.DestinationStream | ||
import io.airbyte.cdk.command.MockCatalogFactory.Companion.stream1 | ||
import io.airbyte.cdk.data.IntegerValue | ||
import io.airbyte.cdk.file.MockTempFileProvider | ||
import io.airbyte.cdk.message.Batch | ||
import io.airbyte.cdk.message.BatchEnvelope | ||
import io.airbyte.cdk.message.Deserializer | ||
import io.airbyte.cdk.message.DestinationMessage | ||
import io.airbyte.cdk.message.DestinationRecord | ||
import io.airbyte.cdk.message.SpilledRawMessagesLocalFile | ||
import io.airbyte.cdk.write.StreamLoader | ||
import io.micronaut.context.annotation.Primary | ||
import io.micronaut.context.annotation.Requires | ||
import io.micronaut.test.extensions.junit5.annotation.MicronautTest | ||
import jakarta.inject.Inject | ||
import jakarta.inject.Singleton | ||
import java.nio.file.Path | ||
import kotlinx.coroutines.test.runTest | ||
import org.junit.jupiter.api.Assertions | ||
import org.junit.jupiter.api.Test | ||
|
||
@MicronautTest(environments = ["ProcessRecordsTaskTest"]) | ||
class ProcessRecordsTaskTest { | ||
@Inject lateinit var taskRunner: TaskRunner | ||
@Inject lateinit var processRecordsTaskFactory: DefaultProcessRecordsTaskFactory | ||
|
||
class MockBatch( | ||
override val state: Batch.State, | ||
val reportedByteSize: Long, | ||
val recordCount: Long, | ||
val pmChecksum: Long, | ||
) : Batch | ||
|
||
class MockStreamLoader : StreamLoader { | ||
override val stream: DestinationStream = stream1 | ||
|
||
data class SumAndCount(val sum: Long = 0, val count: Long = 0) | ||
|
||
override suspend fun processRecords( | ||
records: Iterator<DestinationRecord>, | ||
totalSizeBytes: Long | ||
): Batch { | ||
// Do a simple sum of the record values and count | ||
// To demonstrate that the primed data was actually processed | ||
val (sum, count) = | ||
records.asSequence().fold(SumAndCount()) { acc, record -> | ||
SumAndCount(acc.sum + (record.data as IntegerValue).value, acc.count + 1) | ||
} | ||
return MockBatch( | ||
state = Batch.State.COMPLETE, | ||
reportedByteSize = totalSizeBytes, | ||
recordCount = count, | ||
pmChecksum = sum | ||
) | ||
} | ||
} | ||
|
||
@Singleton | ||
@Primary | ||
@Requires(env = ["ProcessRecordsTaskTest"]) | ||
class MockDeserializer : Deserializer<DestinationMessage> { | ||
override fun deserialize(serialized: String): DestinationMessage { | ||
return DestinationRecord( | ||
stream = stream1, | ||
data = IntegerValue(serialized.toLong()), | ||
emittedAtMs = 0L, | ||
meta = null, | ||
serialized = serialized, | ||
) | ||
} | ||
} | ||
|
||
@Test | ||
fun testProcessRecordsTask() = runTest { | ||
val byteSize = 999L | ||
val recordCount = 1024L | ||
|
||
val launcher = MockTaskLauncher(taskRunner) | ||
val mockFile = | ||
MockTempFileProvider() | ||
.createTempFile(directory = Path.of("tmp/"), prefix = "test", suffix = ".json") | ||
as MockTempFileProvider.MockLocalFile | ||
val file = | ||
SpilledRawMessagesLocalFile( | ||
localFile = mockFile, | ||
totalSizeBytes = byteSize, | ||
) | ||
val task = | ||
processRecordsTaskFactory.make( | ||
taskLauncher = launcher, | ||
streamLoader = MockStreamLoader(), | ||
fileEnvelope = BatchEnvelope(file, Range.closed(0, 1024)) | ||
) | ||
mockFile.linesToRead = (0 until recordCount).map { "$it" }.toMutableList() | ||
|
||
task.execute() | ||
|
||
Assertions.assertEquals(1, launcher.batchEnvelopes.size) | ||
val batch = launcher.batchEnvelopes[0].batch as MockBatch | ||
Assertions.assertEquals(Batch.State.COMPLETE, batch.state) | ||
Assertions.assertEquals(999, batch.reportedByteSize) | ||
Assertions.assertEquals(recordCount, batch.recordCount) | ||
Assertions.assertEquals((0 until recordCount).sum(), batch.pmChecksum) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters