diff --git a/.github/workflows/sandbox-verification.yml b/.github/workflows/sandbox-verification.yml index 7c18979..94d51d2 100644 --- a/.github/workflows/sandbox-verification.yml +++ b/.github/workflows/sandbox-verification.yml @@ -179,15 +179,9 @@ jobs: # (the backend field is String?, not an embedded object). # sys.argv avoids shell-quoting issues with special characters. if [ -z "${params}" ]; then - BODY=$(python3 -c " -import json, sys -print(json.dumps({'columnName': sys.argv[1], 'generatorType': sys.argv[2]})) -" -- "${col}" "${gtype}") + BODY=$(python3 -c "import json, sys; print(json.dumps({'columnName': sys.argv[1], 'generatorType': sys.argv[2]}))" "${col}" "${gtype}") else - BODY=$(python3 -c " -import json, sys -print(json.dumps({'columnName': sys.argv[1], 'generatorType': sys.argv[2], 'generatorParams': sys.argv[3]})) -" -- "${col}" "${gtype}" "${params}") + BODY=$(python3 -c "import json, sys; print(json.dumps({'columnName': sys.argv[1], 'generatorType': sys.argv[2], 'generatorParams': sys.argv[3]}))" "${col}" "${gtype}" "${params}") fi curl -sf -X POST "${API_BASE}/api/workspaces/${WS_ID}/tables/${TABLE_ID}/generators" \ -H "Content-Type: application/json" \ diff --git a/backend/src/main/kotlin/com/opendatamask/application/service/GeneratorService.kt b/backend/src/main/kotlin/com/opendatamask/application/service/GeneratorService.kt index aebd80b..bf4a5fe 100644 --- a/backend/src/main/kotlin/com/opendatamask/application/service/GeneratorService.kt +++ b/backend/src/main/kotlin/com/opendatamask/application/service/GeneratorService.kt @@ -68,7 +68,7 @@ class GeneratorService( GeneratorType.ADDRESS -> faker.address().fullAddress() GeneratorType.SSN -> faker.idNumber().ssnValid() GeneratorType.CREDIT_CARD -> faker.finance().creditCard() - GeneratorType.DATE -> faker.date().past(365 * 10, TimeUnit.DAYS).toString() + GeneratorType.DATE -> java.sql.Date(faker.date().past(365 * 10, TimeUnit.DAYS).time) GeneratorType.UUID -> UUID.randomUUID().toString() GeneratorType.CONSTANT -> params?.get("value") ?: "" GeneratorType.NULL -> null @@ -87,11 +87,11 @@ class GeneratorService( GeneratorType.PASSWORD -> faker.internet().password(8, 20, true, true, true) GeneratorType.IBAN -> faker.finance().iban() GeneratorType.SWIFT_CODE -> faker.finance().bic() - GeneratorType.MONEY_AMOUNT -> faker.commerce().price() + GeneratorType.MONEY_AMOUNT -> java.math.BigDecimal(faker.commerce().price()) GeneratorType.BTC_ADDRESS -> faker.regexify("1[A-HJ-NP-Za-km-z1-9]{33}") GeneratorType.PASSPORT_NUMBER -> faker.regexify("[A-Z]{2}[0-9]{7}") GeneratorType.DRIVERS_LICENSE -> faker.regexify("[A-Z][0-9]{7}") - GeneratorType.BIRTH_DATE -> faker.date().birthday().toString() + GeneratorType.BIRTH_DATE -> java.sql.Date(faker.date().birthday().time) GeneratorType.GENDER -> listOf("Male", "Female", "Non-binary", "Prefer not to say").random() GeneratorType.ICD_CODE -> faker.regexify("[A-Z][0-9]{2}\\.[0-9]{1,2}") GeneratorType.MEDICAL_RECORD_NUMBER -> faker.regexify("MRN-[0-9]{8}") @@ -136,12 +136,12 @@ class GeneratorService( val step = params?.get("step")?.toLongOrNull() ?: 1L val key = columnKey ?: "default" val counter = sequentialCounters.computeIfAbsent(key) { AtomicLong(start - step) } - counter.addAndGet(step).toString() + counter.addAndGet(step) } GeneratorType.RANDOM_INT -> { val min = params?.get("min")?.toLongOrNull() ?: 1L val max = params?.get("max")?.toLongOrNull() ?: 999999L - faker.number().numberBetween(min, max).toString() + faker.number().numberBetween(min, max) } GeneratorType.CONDITIONAL -> { val jsonParams = rawParams?.let { diff --git a/backend/src/test/kotlin/com/opendatamask/application/service/GeneratorServiceCompositeTest.kt b/backend/src/test/kotlin/com/opendatamask/application/service/GeneratorServiceCompositeTest.kt index 28fc734..baa8a09 100644 --- a/backend/src/test/kotlin/com/opendatamask/application/service/GeneratorServiceCompositeTest.kt +++ b/backend/src/test/kotlin/com/opendatamask/application/service/GeneratorServiceCompositeTest.kt @@ -142,55 +142,53 @@ class GeneratorServiceCompositeTest { null, mapOf("start" to "10", "step" to "1"), columnKey = "test:seq_col_start" - ) as String - assertEquals("10", result) + ) as Long + assertEquals(10L, result) } @Test fun `SEQUENTIAL increments by step`() { val key = "test:seq_col_step" - val r1 = service.generateValue(GeneratorType.SEQUENTIAL, null, mapOf("start" to "1", "step" to "5"), columnKey = key) as String - val r2 = service.generateValue(GeneratorType.SEQUENTIAL, null, mapOf("start" to "1", "step" to "5"), columnKey = key) as String - val r3 = service.generateValue(GeneratorType.SEQUENTIAL, null, mapOf("start" to "1", "step" to "5"), columnKey = key) as String - assertEquals("1", r1) - assertEquals("6", r2) - assertEquals("11", r3) + val r1 = service.generateValue(GeneratorType.SEQUENTIAL, null, mapOf("start" to "1", "step" to "5"), columnKey = key) as Long + val r2 = service.generateValue(GeneratorType.SEQUENTIAL, null, mapOf("start" to "1", "step" to "5"), columnKey = key) as Long + val r3 = service.generateValue(GeneratorType.SEQUENTIAL, null, mapOf("start" to "1", "step" to "5"), columnKey = key) as Long + assertEquals(1L, r1) + assertEquals(6L, r2) + assertEquals(11L, r3) } @Test fun `SEQUENTIAL uses separate counters per column key`() { - val r1 = service.generateValue(GeneratorType.SEQUENTIAL, null, mapOf("start" to "1", "step" to "1"), columnKey = "table:col_a") as String - val r2 = service.generateValue(GeneratorType.SEQUENTIAL, null, mapOf("start" to "1", "step" to "1"), columnKey = "table:col_b") as String - assertEquals("1", r1) - assertEquals("1", r2, "Different column keys should have independent counters") + val r1 = service.generateValue(GeneratorType.SEQUENTIAL, null, mapOf("start" to "1", "step" to "1"), columnKey = "table:col_a") as Long + val r2 = service.generateValue(GeneratorType.SEQUENTIAL, null, mapOf("start" to "1", "step" to "1"), columnKey = "table:col_b") as Long + assertEquals(1L, r1) + assertEquals(1L, r2, "Different column keys should have independent counters") } // ── RANDOM_INT ──────────────────────────────────────────────────────────── @Test - fun `RANDOM_INT returns string within range`() { + fun `RANDOM_INT returns Long within range`() { val result = service.generateValue( GeneratorType.RANDOM_INT, null, mapOf("min" to "1000", "max" to "9999999") - ) as String - val num = result.toLong() - assertTrue(num >= 1000, "Result $num should be >= 1000") - assertTrue(num <= 9999999, "Result $num should be <= 9999999") + ) as Long + assertTrue(result >= 1000, "Result $result should be >= 1000") + assertTrue(result <= 9999999, "Result $result should be <= 9999999") } @Test - fun `RANDOM_INT returns a string not an integer`() { + fun `RANDOM_INT returns a Long not a String`() { val result = service.generateValue(GeneratorType.RANDOM_INT, null, mapOf("min" to "1", "max" to "100")) assertNotNull(result) - assertTrue(result is String, "RANDOM_INT should return a String, got ${result?.javaClass}") + assertTrue(result is Long, "RANDOM_INT should return a Long, got ${result?.javaClass}") } @Test fun `RANDOM_INT uses defaults when no params`() { - val result = service.generateValue(GeneratorType.RANDOM_INT, null, null) as String - val num = result.toLong() - assertTrue(num >= 1) - assertTrue(num <= 999999) + val result = service.generateValue(GeneratorType.RANDOM_INT, null, null) as Long + assertTrue(result >= 1) + assertTrue(result <= 999999) } } diff --git a/backend/src/test/kotlin/com/opendatamask/application/service/GeneratorServiceTest.kt b/backend/src/test/kotlin/com/opendatamask/application/service/GeneratorServiceTest.kt index ee32bc9..8e31239 100644 --- a/backend/src/test/kotlin/com/opendatamask/application/service/GeneratorServiceTest.kt +++ b/backend/src/test/kotlin/com/opendatamask/application/service/GeneratorServiceTest.kt @@ -58,9 +58,17 @@ class GeneratorServiceTest { } @Test - fun `DATE generates a non-null date string`() { + fun `DATE generates a java sql Date`() { val result = service.generateValue(GeneratorType.DATE, "2024-01-01", null) assertNotNull(result) + assertInstanceOf(java.sql.Date::class.java, result) + } + + @Test + fun `BIRTH_DATE generates a java sql Date`() { + val result = service.generateValue(GeneratorType.BIRTH_DATE, "1990-01-15", null) + assertNotNull(result) + assertInstanceOf(java.sql.Date::class.java, result) } @Test @@ -170,4 +178,28 @@ class GeneratorServiceTest { val result = service.generateValue(GeneratorType.CUSTOM, "fallback", null) assertEquals("fallback", result) } + + @Test + fun `RANDOM_INT generates a Long`() { + val result = service.generateValue(GeneratorType.RANDOM_INT, null, mapOf("min" to "30000", "max" to "200000")) + assertNotNull(result) + assertInstanceOf(java.lang.Long::class.java, result) + val value = result as Long + assertTrue(value in 30000..199999) + } + + @Test + fun `SEQUENTIAL generates a Long`() { + val result = service.generateValue(GeneratorType.SEQUENTIAL, null, mapOf("start" to "1", "step" to "1"), columnKey = "test:seq") + assertNotNull(result) + assertInstanceOf(java.lang.Long::class.java, result) + assertEquals(1L, result) + } + + @Test + fun `MONEY_AMOUNT generates a BigDecimal`() { + val result = service.generateValue(GeneratorType.MONEY_AMOUNT, null, null) + assertNotNull(result) + assertInstanceOf(java.math.BigDecimal::class.java, result) + } }