diff --git a/extensions/tn_attestation/harness_integration_test.go b/extensions/tn_attestation/harness_integration_test.go index ce6c64219..516bf4696 100644 --- a/extensions/tn_attestation/harness_integration_test.go +++ b/extensions/tn_attestation/harness_integration_test.go @@ -80,7 +80,7 @@ func TestSigningWorkflowWithHarness(t *testing.T) { kwilTesting.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "tn_attestation_signing_harness", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), Owner: ownerAddr.Address(), FunctionTests: []kwilTesting.TestFunc{ func(ctx context.Context, platform *kwilTesting.Platform) error { diff --git a/extensions/tn_cache/internal/engine_ops_integration_test.go b/extensions/tn_cache/internal/engine_ops_integration_test.go index e31a80b40..29ebcba2f 100644 --- a/extensions/tn_cache/internal/engine_ops_integration_test.go +++ b/extensions/tn_cache/internal/engine_ops_integration_test.go @@ -32,7 +32,7 @@ func generateTestStreamID(prefix string) util.StreamId { func TestEngineOperations_Integration(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "engine_ops_integration_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testEngineOperationsIntegration(t), }, @@ -331,7 +331,7 @@ func testEngineOperationsIntegration(t *testing.T) func(ctx context.Context, pla func TestEngineOperations_RealTimeData(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "tn_ops_realtime_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testEngineOperationsRealTime(t), }, diff --git a/extensions/tn_cache/internal/engine_ops_permissions_test.go b/extensions/tn_cache/internal/engine_ops_permissions_test.go index cb55685fb..cc7580bcc 100644 --- a/extensions/tn_cache/internal/engine_ops_permissions_test.go +++ b/extensions/tn_cache/internal/engine_ops_permissions_test.go @@ -26,7 +26,7 @@ import ( func TestExtensionAgentPermissions(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "extension_agent_permissions_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testExtensionAgentAccess(t), }, diff --git a/internal/benchmark/digest/digest_benchmark_test.go b/internal/benchmark/digest/digest_benchmark_test.go index 683b5e507..91083bd84 100644 --- a/internal/benchmark/digest/digest_benchmark_test.go +++ b/internal/benchmark/digest/digest_benchmark_test.go @@ -198,7 +198,7 @@ func createBenchmarkSuiteFunc(t *testing.T, scale DigestBenchmarkScale) func(con func runDigestBenchmarkScale(t *testing.T, scale DigestBenchmarkScale) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: fmt.Sprintf("digest_benchmark_%s", scale.Name), - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ WithDigestBenchmarkSetup(createBenchmarkSuiteFunc(t, scale)), }, diff --git a/internal/benchmark/load_test.go b/internal/benchmark/load_test.go index d2b35f36b..4ff2e60ff 100644 --- a/internal/benchmark/load_test.go +++ b/internal/benchmark/load_test.go @@ -241,9 +241,9 @@ func TestBench(t *testing.T) { groupStartTime := time.Now() schemaTest := kwilTesting.SchemaTest{ - Name: groupName, - FunctionTests: groupOfTests, - SeedScripts: migrations.GetSeedScriptPaths(), + Name: groupName, + FunctionTests: groupOfTests, + SeedStatements: migrations.GetSeedScriptStatements(), } t.Run(schemaTest.Name, func(t *testing.T) { diff --git a/internal/migrations/001-common-actions.sql b/internal/migrations/001-common-actions.sql index 6c243d3ac..67f31f39a 100644 --- a/internal/migrations/001-common-actions.sql +++ b/internal/migrations/001-common-actions.sql @@ -48,6 +48,8 @@ CREATE OR REPLACE ACTION create_stream( /** * create_streams: Creates multiple streams at once. + * Fee: 2 TRUF per stream created + * Exemption: system:network_writer role bypasses fee collection * Validates stream_id format, data provider address, and stream type. * Sets default metadata including type, owner, visibility, and readonly keys. */ @@ -55,20 +57,37 @@ CREATE OR REPLACE ACTION create_streams( $stream_ids TEXT[], $stream_types TEXT[] ) PUBLIC { + -- ===== FEE COLLECTION WITH ROLE EXEMPTION ===== $lower_caller TEXT := LOWER(@caller); - -- Permission Check: Ensure caller has the 'system:network_writer' role. - $has_permission BOOL := false; - for $row in are_members_of('system', 'network_writer', ARRAY[$lower_caller]) { - if $row.wallet = $lower_caller AND $row.is_member { - $has_permission := true; - break; + + -- Check if caller is exempt (has system:network_writer role) + $is_exempt BOOL := FALSE; + FOR $row IN are_members_of('system', 'network_writer', ARRAY[$lower_caller]) { + IF $row.wallet = $lower_caller AND $row.is_member { + $is_exempt := TRUE; + BREAK; } } - if NOT $has_permission { - ERROR('Caller does not have the required system:network_writer role to create streams.'); + + -- Collect fee only from non-exempt wallets (2 TRUF per stream) + IF NOT $is_exempt { + $fee_per_stream := 2000000000000000000::NUMERIC(78, 0); -- 2 TRUF with 18 decimals + $num_streams := array_length($stream_ids); + $total_fee := $fee_per_stream * $num_streams::NUMERIC(78, 0); + + $caller_balance := ethereum_bridge.balance(@caller); + + IF $caller_balance < $total_fee { + ERROR('Insufficient balance for stream creation. Required: ' || ($num_streams * 2)::TEXT || ' TRUF for ' || $num_streams::TEXT || ' stream(s)'); + } + + $leader_addr TEXT := encode(@leader_sender, 'hex')::TEXT; + ethereum_bridge.transfer($leader_addr, $total_fee); } + -- ===== END FEE COLLECTION ===== - -- Get caller's address (data provider) first + -- ===== STREAM CREATION LOGIC ===== + -- Get caller's address (data provider) $data_provider TEXT := $lower_caller; -- Check if caller is a valid ethereum address diff --git a/internal/migrations/migration.go b/internal/migrations/migration.go index 1226ea590..f722ccca1 100644 --- a/internal/migrations/migration.go +++ b/internal/migrations/migration.go @@ -46,3 +46,64 @@ func GetSeedScriptPaths() []string { sort.Strings(seedFilesPaths) return seedFilesPaths } + +// GetSeedScriptStatements returns migration SQL statements with test-specific replacements. +// This is used for test environments to replace production bridge namespace with test bridge. +// +// Replacement strategy: +// - Production migrations use 'ethereum_bridge' (mainnet) +// - Test environments need 'sepolia_bridge' (testnet) +// - Runtime string replacement avoids duplicate migration files +// +// This implements the approach discussed to avoid maintaining separate test-only override files +// that duplicate logic and can become a bug source if production and test logic diverges. +func GetSeedScriptStatements() []string { + var statements []string + + // Collect all SQL file paths in sorted order + var sqlPaths []string + err := fs.WalkDir(seedFiles, ".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + return nil + } + if !strings.HasSuffix(path, ".sql") { + return nil + } + if strings.HasSuffix(path, ".prod.sql") { + return nil + } + sqlPaths = append(sqlPaths, path) + return nil + }) + if err != nil { + panic(err) + } + + if len(sqlPaths) == 0 { + panic("no SQL files found in embedded directory") + } + + // Sort paths to ensure consistent loading order (0_test_only/ loads after production) + sort.Strings(sqlPaths) + + // Read each file and apply test-specific replacements + for _, path := range sqlPaths { + content, err := seedFiles.ReadFile(path) + if err != nil { + panic("failed to read migration file " + path + ": " + err.Error()) + } + + sql := string(content) + + // Replace production bridge namespace with test bridge namespace + // This allows tests to use the same migration logic without duplication + sql = strings.ReplaceAll(sql, "ethereum_bridge", "sepolia_bridge") + + statements = append(statements, sql) + } + + return statements +} diff --git a/tests/database_size/database_size_v2_test.go b/tests/database_size/database_size_v2_test.go index 0fe546fc7..77165992a 100644 --- a/tests/database_size/database_size_v2_test.go +++ b/tests/database_size/database_size_v2_test.go @@ -35,7 +35,7 @@ import ( func TestDatabaseSizeV2Actions(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "database_size_v2_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testGetDatabaseSizeV2(t), testGetDatabaseSizeV2Pretty(t), diff --git a/tests/extensions/database-size/database_size_extension_test.go b/tests/extensions/database-size/database_size_extension_test.go index 57c411b95..be00aeacd 100644 --- a/tests/extensions/database-size/database_size_extension_test.go +++ b/tests/extensions/database-size/database_size_extension_test.go @@ -27,7 +27,7 @@ func TestDatabaseSizeExtension(t *testing.T) { // The database_size extension should be automatically available testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "database_size_extension_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testDatabaseSizeExtensionMethods(t), testGetDatabaseSizeV2Actions(t), diff --git a/tests/extensions/erc20/common_test.go b/tests/extensions/erc20/common_test.go index 53cc9697d..1deec0fcd 100644 --- a/tests/extensions/erc20/common_test.go +++ b/tests/extensions/erc20/common_test.go @@ -28,7 +28,7 @@ const ( // seedAndRun is a helper that handles test execution with proper isolation func seedAndRun(t TestingT, name string, fn kwilTesting.TestFunc) { - seedScripts := migrations.GetSeedScriptPaths() + seedStatements := migrations.GetSeedScriptStatements() // Wrap the test function to add singleton reset and cleanup wrappedFn := func(ctx context.Context, platform *kwilTesting.Platform) error { @@ -50,9 +50,9 @@ func seedAndRun(t TestingT, name string, fn kwilTesting.TestFunc) { } testutils.RunSchemaTest(t.(*testing.T), kwilTesting.SchemaTest{ - Name: name, - SeedScripts: seedScripts, - FunctionTests: []kwilTesting.TestFunc{wrappedFn}, + Name: name, + SeedStatements: seedStatements, + FunctionTests: []kwilTesting.TestFunc{wrappedFn}, }, &testutils.Options{Options: testutils.GetTestOptions()}) } diff --git a/tests/extensions/tn_cache/cache_height_tracking_test.go b/tests/extensions/tn_cache/cache_height_tracking_test.go index 39fd59fee..c3091c69c 100644 --- a/tests/extensions/tn_cache/cache_height_tracking_test.go +++ b/tests/extensions/tn_cache/cache_height_tracking_test.go @@ -32,7 +32,7 @@ func TestCacheHeightTracking(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "cache_height_tracking_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ func(ctx context.Context, platform *kwilTesting.Platform) error { helper := cache.SetupCacheTest(ctx, platform, cacheConfig) diff --git a/tests/extensions/tn_cache/cache_integration_test.go b/tests/extensions/tn_cache/cache_integration_test.go index f12e45936..dbf6a2f2b 100644 --- a/tests/extensions/tn_cache/cache_integration_test.go +++ b/tests/extensions/tn_cache/cache_integration_test.go @@ -36,7 +36,7 @@ func TestCacheIntegration(t *testing.T) { // Run the test with cache enabled using the new wrapper testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "cache_integration_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testCacheBasicFunctionality(t, cacheConfig), }, @@ -218,7 +218,7 @@ func TestCacheIncludeChildrenForNestedComposed(t *testing.T) { // Run the test with cache enabled testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "cache_include_children_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testCacheIncludeChildren(t, cacheConfig), }, @@ -240,7 +240,7 @@ func TestCacheBaseTimeVariants(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "cache_base_time_variants_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testCacheBaseTimeVariants(t, cacheConfig, streamID, directiveBaseTime, lateFrom), }, diff --git a/tests/extensions/tn_cache/cache_observability_test.go b/tests/extensions/tn_cache/cache_observability_test.go index b506a3cab..fcc678b46 100644 --- a/tests/extensions/tn_cache/cache_observability_test.go +++ b/tests/extensions/tn_cache/cache_observability_test.go @@ -31,7 +31,7 @@ func TestCacheObservability(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "cache_observability_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ func(ctx context.Context, platform *kwilTesting.Platform) error { // Cache is already set up by the wrapper, but we need the helper for RefreshCache diff --git a/tests/extensions/tn_cache/resolution_transaction_test.go b/tests/extensions/tn_cache/resolution_transaction_test.go index 1f0719695..00228cb42 100644 --- a/tests/extensions/tn_cache/resolution_transaction_test.go +++ b/tests/extensions/tn_cache/resolution_transaction_test.go @@ -25,7 +25,7 @@ func TestResolutionInTransaction(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "resolution_transaction_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ func(ctx context.Context, platform *kwilTesting.Platform) error { platform = procedure.WithSigner(platform, deployer.Bytes()) diff --git a/tests/streams/aggregation/aggr01_basic_aggregation_test.go b/tests/streams/aggregation/aggr01_basic_aggregation_test.go index 8db5b8498..82516fafe 100644 --- a/tests/streams/aggregation/aggr01_basic_aggregation_test.go +++ b/tests/streams/aggregation/aggr01_basic_aggregation_test.go @@ -34,7 +34,7 @@ func TestAGGR01_BasicAggregation(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "aggr01_basic_aggregation_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ wrapTestWithCacheModes(t, "AGGR01_BasicAggregation", testAGGR01_BasicAggregation), }, diff --git a/tests/streams/aggregation/aggr02_weighted_contributions_test.go b/tests/streams/aggregation/aggr02_weighted_contributions_test.go index c84e20c8a..ded19ccc7 100644 --- a/tests/streams/aggregation/aggr02_weighted_contributions_test.go +++ b/tests/streams/aggregation/aggr02_weighted_contributions_test.go @@ -34,7 +34,7 @@ func TestAGGR02_WeightedContributions(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "aggr02_weighted_contributions_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ wrapTestWithCacheModes(t, "AGGR02_WeightedContributions", testAGGR02_WeightedContributions), }, diff --git a/tests/streams/aggregation/aggr03_taxonomy_validity_periods_test.go b/tests/streams/aggregation/aggr03_taxonomy_validity_periods_test.go index 6502ad60d..8238a01e8 100644 --- a/tests/streams/aggregation/aggr03_taxonomy_validity_periods_test.go +++ b/tests/streams/aggregation/aggr03_taxonomy_validity_periods_test.go @@ -37,7 +37,7 @@ func TestAGGR03_TaxonomyValidityPeriods(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "aggr03_taxonomy_validity_periods_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ wrapTestWithCacheModes(t, "AGGR03_TaxonomyValidityPeriods", testAGGR03_TaxonomyValidityPeriods), }, diff --git a/tests/streams/aggregation/aggr04_missing_data_handling_test.go b/tests/streams/aggregation/aggr04_missing_data_handling_test.go index f15de9300..964eda530 100644 --- a/tests/streams/aggregation/aggr04_missing_data_handling_test.go +++ b/tests/streams/aggregation/aggr04_missing_data_handling_test.go @@ -36,7 +36,7 @@ func TestAGGR04_MissingDataHandling(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "aggr04_missing_data_handling_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ wrapTestWithCacheModes(t, "AGGR04_MissingDataHandling", testAGGR04_MissingDataHandling), }, diff --git a/tests/streams/aggregation/aggr05_no_duplicate_child_streams_test.go b/tests/streams/aggregation/aggr05_no_duplicate_child_streams_test.go index 28167e604..201ad5d41 100644 --- a/tests/streams/aggregation/aggr05_no_duplicate_child_streams_test.go +++ b/tests/streams/aggregation/aggr05_no_duplicate_child_streams_test.go @@ -30,7 +30,7 @@ func TestAGGR05_NoDuplicateChildStreams(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "aggr05_no_duplicate_child_streams_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ wrapTestWithCacheModes(t, "AGGR05_NoDuplicateChildStreams", testAGGR05_NoDuplicateChildStreams), }, diff --git a/tests/streams/aggregation/aggr06_single_active_taxonomy_test.go b/tests/streams/aggregation/aggr06_single_active_taxonomy_test.go index f35a9d5dd..f76a2b772 100644 --- a/tests/streams/aggregation/aggr06_single_active_taxonomy_test.go +++ b/tests/streams/aggregation/aggr06_single_active_taxonomy_test.go @@ -33,7 +33,7 @@ func TestAGGR06_SingleActiveTaxonomy(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "aggr06_single_active_taxonomy_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ wrapTestWithCacheModes(t, "AGGR06_SingleActiveTaxonomy", testAGGR06_SingleActiveTaxonomy), }, diff --git a/tests/streams/aggregation/aggr07_inexistent_streams_test.go b/tests/streams/aggregation/aggr07_inexistent_streams_test.go index eef8d0710..803e30566 100644 --- a/tests/streams/aggregation/aggr07_inexistent_streams_test.go +++ b/tests/streams/aggregation/aggr07_inexistent_streams_test.go @@ -29,7 +29,7 @@ func TestAGGR07_InexistentStreamsRejected(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "aggr07_inexistent_streams_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ wrapTestWithCacheModes(t, "AGGR07_InexistentStreams", testAGGR07_InexistentStreams), }, diff --git a/tests/streams/aggregation/aggr08_weight_change_test.go b/tests/streams/aggregation/aggr08_weight_change_test.go index 36d73a64a..82ac8bbad 100644 --- a/tests/streams/aggregation/aggr08_weight_change_test.go +++ b/tests/streams/aggregation/aggr08_weight_change_test.go @@ -41,7 +41,7 @@ func TestAGGR08_WeightChangeEventPoints(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "aggr08_weight_change_event_points_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ wrapTestWithCacheModes(t, "AGGR08_WeightChangeEventPoints", testAGGR08_WeightChangeEventPoints), }, diff --git a/tests/streams/aggregation/aggr09_duplicate_values_test.go b/tests/streams/aggregation/aggr09_duplicate_values_test.go index a0711621a..35a595f6a 100644 --- a/tests/streams/aggregation/aggr09_duplicate_values_test.go +++ b/tests/streams/aggregation/aggr09_duplicate_values_test.go @@ -43,7 +43,7 @@ func TestAGGR09_DuplicateValues(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "aggr09_duplicate_values_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ wrapTestWithCacheModes(t, "AGGR09_DuplicateValues", testAGGR09_DuplicateValues), }, diff --git a/tests/streams/attestation/attestation_request_test.go b/tests/streams/attestation/attestation_request_test.go index fd6579813..3c280c50b 100644 --- a/tests/streams/attestation/attestation_request_test.go +++ b/tests/streams/attestation/attestation_request_test.go @@ -25,7 +25,7 @@ func TestRequestAttestationInsertsCanonicalPayload(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "ATTESTATION01_RequestInsertion", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), Owner: addrs.Owner.Address(), FunctionTests: []kwilTesting.TestFunc{ func(ctx context.Context, platform *kwilTesting.Platform) error { diff --git a/tests/streams/attestation/attestation_retrieval_test.go b/tests/streams/attestation/attestation_retrieval_test.go index bbcc3bb93..b8e037e49 100644 --- a/tests/streams/attestation/attestation_retrieval_test.go +++ b/tests/streams/attestation/attestation_retrieval_test.go @@ -20,7 +20,7 @@ func TestGetSignedAttestation(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "ATTESTATION02_GetSignedAttestation", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), Owner: addrs.Owner.Address(), FunctionTests: []kwilTesting.TestFunc{ func(ctx context.Context, platform *kwilTesting.Platform) error { @@ -108,7 +108,7 @@ func TestListAttestations(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "ATTESTATION03_ListAttestations", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), Owner: addrs.Owner.Address(), FunctionTests: []kwilTesting.TestFunc{ func(ctx context.Context, platform *kwilTesting.Platform) error { diff --git a/tests/streams/auth/auth_test.go b/tests/streams/auth/auth_test.go index 3bef5a2ad..d545c6798 100644 --- a/tests/streams/auth/auth_test.go +++ b/tests/streams/auth/auth_test.go @@ -44,7 +44,7 @@ func TestAUTH01_StreamOwnership(t *testing.T) { t.Run("ValidOwnershipTransfer", func(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "stream_ownership_transfer_AUTH01", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testStreamOwnershipTransfer(t), }, @@ -55,7 +55,7 @@ func TestAUTH01_StreamOwnership(t *testing.T) { t.Run("InvalidAddressHandling", func(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "invalid_address_ownership_transfer_AUTH01", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testInvalidAddressOwnershipTransfer(t), }, @@ -176,7 +176,7 @@ func testInvalidAddressOwnershipTransfer(t *testing.T) kwilTesting.TestFunc { func TestAUTH02_ReadPermissions(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "read_permission_control_AUTH02", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testReadPermissionControl(t, primitiveStreamInfo), testReadPermissionControl(t, composedStreamInfo), @@ -297,7 +297,7 @@ func expectedVerb(canRead bool) string { func TestAUTH02_NestedReadPermissions(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "nested_read_permission_control_AUTH02", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testNestedReadPermissionControl(t), }, @@ -462,7 +462,7 @@ func TestAUTH03_WritePermissions(t *testing.T) { testWritePermissionControl(t, primitiveStreamInfo), testWritePermissionControl(t, composedStreamInfo), }, - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), }, testutils.GetTestOptionsWithCache()) } @@ -531,7 +531,7 @@ func testWritePermissionControl(t *testing.T, streamInfo setup.StreamInfo) kwilT func TestAUTH04_ComposePermissions(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "compose_permission_control_AUTH04", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testComposePermissionControl(t, primitiveStreamInfo), testComposePermissionControl(t, composedStreamInfo), @@ -628,7 +628,7 @@ func testComposePermissionControl(t *testing.T, contractInfo setup.StreamInfo) k func TestAUTH04_NestedComposePermissions(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "nested_compose_permission_control_AUTH04", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testNestedComposePermissionControl(t), }, @@ -856,7 +856,7 @@ func testNestedComposePermissionControl(t *testing.T) kwilTesting.TestFunc { func TestAUTH05_StreamDeletion(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "stream_deletion_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testStreamDeletion(t), }, @@ -923,7 +923,7 @@ func testStreamDeletion(t *testing.T) kwilTesting.TestFunc { func TestFilterStreamsByExistence(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "filter_streams_by_existence_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testFilterStreamsByExistence(t), }, diff --git a/tests/streams/backward_compatibility_test.go b/tests/streams/backward_compatibility_test.go index 102259563..993cf93ed 100644 --- a/tests/streams/backward_compatibility_test.go +++ b/tests/streams/backward_compatibility_test.go @@ -15,7 +15,7 @@ import ( func TestBackwardCompatibility(t *testing.T) { testutils.RunSchemaTest(t, kwiltesting.SchemaTest{ Name: "backward_compatibility_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwiltesting.TestFunc{ testBackwardCompatibilityFunc(t), }, diff --git a/tests/streams/cache_base_time_variants_test.go b/tests/streams/cache_base_time_variants_test.go index 8b82bde3e..996871aaa 100644 --- a/tests/streams/cache_base_time_variants_test.go +++ b/tests/streams/cache_base_time_variants_test.go @@ -32,7 +32,7 @@ func TestStreamCacheBaseTimeVariants(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "stream_cache_base_time_variants", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ runStreamCacheBaseTimeVariants(t, cacheConfig, baseStreamID, explicitBaseTime), }, diff --git a/tests/streams/common_test.go b/tests/streams/common_test.go index 1ac5f0827..a3f99c891 100644 --- a/tests/streams/common_test.go +++ b/tests/streams/common_test.go @@ -43,7 +43,7 @@ var ( func TestCOMMON03DisableMetadata(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "disable_metadata", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testDisableMetadata(t, primitiveStreamInfo), testDisableMetadata(t, composedStreamInfo), @@ -117,7 +117,7 @@ func testDisableMetadata(t *testing.T, streamInfo setup.StreamInfo) kwilTesting. func TestCOMMON02ReadOnlyMetadataCannotBeModified(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "readonly_metadata_cannot_be_modified", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testReadOnlyMetadataCannotBeModified(t, primitiveStreamInfo), testReadOnlyMetadataCannotBeModified(t, composedStreamInfo), @@ -177,7 +177,7 @@ func testReadOnlyMetadataCannotBeModified(t *testing.T, streamInfo setup.StreamI func TestCOMMON02BReadOnlyMetadataCannotBeModified_Batch(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "readonly_metadata_cannot_be_modified_batch", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testReadOnlyMetadataCannotBeModifiedBatch(t, primitiveStreamInfo), testReadOnlyMetadataCannotBeModifiedBatch(t, composedStreamInfo), @@ -243,7 +243,7 @@ func TestVisibilitySettings(t *testing.T) { testVisibilitySettings(t, primitiveStreamInfo), testVisibilitySettings(t, composedStreamInfo), }, - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), }, testutils.GetTestOptionsWithCache()) } diff --git a/tests/streams/complex_composed_test.go b/tests/streams/complex_composed_test.go index a844a17ab..7c3baaf16 100644 --- a/tests/streams/complex_composed_test.go +++ b/tests/streams/complex_composed_test.go @@ -32,7 +32,7 @@ func TestComplexComposed(t *testing.T) { cacheConfig := testutils.SimpleCache(complexComposedDeployer.Address(), "*") testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "complex_composed_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ WithTestSetup(wrapTestWithCacheModes(t, "ComplexComposedRecord", testComplexComposedRecord)), WithTestSetup(wrapTestWithCacheModes(t, "ComplexComposedIndex", testComplexComposedIndex)), diff --git a/tests/streams/composed_test.go b/tests/streams/composed_test.go index 5743fe690..614277da8 100644 --- a/tests/streams/composed_test.go +++ b/tests/streams/composed_test.go @@ -26,7 +26,7 @@ var ( func TestComposed(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "composed_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testComposedLastAvailable(t), testCOMPOSED01SetTaxonomyWithValidData(t), diff --git a/tests/streams/comprehensive_shared_path_independence_test.go b/tests/streams/comprehensive_shared_path_independence_test.go index 49c347c98..244127493 100644 --- a/tests/streams/comprehensive_shared_path_independence_test.go +++ b/tests/streams/comprehensive_shared_path_independence_test.go @@ -34,7 +34,7 @@ var ( func TestComprehensivePathIndependenceWithSharedPrimitive(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "comprehensive_path_independence_shared_primitive_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ setupComprehensiveSharedStreams(testComprehensivePathIndependenceSharedFunc(t)), }, diff --git a/tests/streams/database_size_test.go b/tests/streams/database_size_test.go index 7d3dcddfb..4a1e30e25 100644 --- a/tests/streams/database_size_test.go +++ b/tests/streams/database_size_test.go @@ -25,7 +25,7 @@ var ( func TestDatabaseSize(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "database_size_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ WithTestDatabaseSizeSetup(testDatabaseSize(t)), }, diff --git a/tests/streams/digest/digest_actions_test.go b/tests/streams/digest/digest_actions_test.go index 7275990d5..6ac839392 100644 --- a/tests/streams/digest/digest_actions_test.go +++ b/tests/streams/digest/digest_actions_test.go @@ -34,7 +34,7 @@ var idempotencyTestStreamId = util.GenerateStreamId(idempotencyTestStreamName) func TestDigestActions(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "digest_actions_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ WithDigestTestSetup(testDigestBasicOHLCCalculation(t)), WithDigestTestSetup(testGetDailyOHLCRawData(t)), @@ -68,7 +68,7 @@ func TestDigestActions(t *testing.T) { func TestDigestActionsLeaderAuthorization(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "digest_actions_leader_authorization", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ WithSignerAndProvider(func(ctx context.Context, platform *kwilTesting.Platform) error { // Create a secp256k1 leader key diff --git a/tests/streams/gamefi_index_test.go b/tests/streams/gamefi_index_test.go index 0e469097e..ddfd9b825 100644 --- a/tests/streams/gamefi_index_test.go +++ b/tests/streams/gamefi_index_test.go @@ -100,7 +100,7 @@ var ( func TestGamefiIndex(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "gamefi_index_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ WithGamefiTestSetup(testGamefiIndexCalculation(t)), WithGamefiProportionalTestSetup(testGamefiIndexProportionalCalculation(t)), diff --git a/tests/streams/index_change_test.go b/tests/streams/index_change_test.go index c03155aa9..8d53fe0fa 100644 --- a/tests/streams/index_change_test.go +++ b/tests/streams/index_change_test.go @@ -24,7 +24,7 @@ var ( func TestIndexChange(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "index_change_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ withTestIndexChangeSetup(testIndexChange(t)), withTestIndexChangeSetup(testYoYIndexChange(t)), diff --git a/tests/streams/multi_level_composed_test.go b/tests/streams/multi_level_composed_test.go index 9f362b545..92f1858e2 100644 --- a/tests/streams/multi_level_composed_test.go +++ b/tests/streams/multi_level_composed_test.go @@ -34,7 +34,7 @@ var ( func TestMultiLevelComposedStreams(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "multi_level_composed_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ setupMultiLevelComposedStreams(testMultiLevelFunc(t)), }, diff --git a/tests/streams/other/other_test.go b/tests/streams/other/other_test.go index 60df041fc..ef6ee5d6f 100644 --- a/tests/streams/other/other_test.go +++ b/tests/streams/other/other_test.go @@ -30,7 +30,7 @@ var defaultStreamLocator = types.StreamLocator{ func TestAddressValidation(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "address_validation_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ func(ctx context.Context, platform *kwilTesting.Platform) error { // Test valid address - should succeed @@ -90,7 +90,7 @@ func TestAddressValidation(t *testing.T) { func TestStreamIDValidation(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "stream_id_validation_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ func(ctx context.Context, platform *kwilTesting.Platform) error { // Test stream ID format validation @@ -201,7 +201,7 @@ func TestStreamIDValidation(t *testing.T) { func TestAnyUserCanCreateStream(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "any_user_can_create_stream_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testAnyUserCanCreateStream(t), testAnyUserCanCreateStream(t), @@ -252,7 +252,7 @@ func testAnyUserCanCreateStream(t *testing.T) func(ctx context.Context, platform func TestMultipleStreamCreation(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "multiple_stream_creation_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testMultipleStreamCreation(t), }, diff --git a/tests/streams/other/stream_exists_batch_test.go b/tests/streams/other/stream_exists_batch_test.go index a81a0c384..13e1e2979 100644 --- a/tests/streams/other/stream_exists_batch_test.go +++ b/tests/streams/other/stream_exists_batch_test.go @@ -24,7 +24,7 @@ import ( func TestSTREAM_EXISTS_BATCH01_Direct(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "stream_exists_batch_direct_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testStreamExistsBatchDirect(t), }, diff --git a/tests/streams/pending_prune_days_test.go b/tests/streams/pending_prune_days_test.go index 8a7afd641..d134bc145 100644 --- a/tests/streams/pending_prune_days_test.go +++ b/tests/streams/pending_prune_days_test.go @@ -23,7 +23,7 @@ import ( func TestPendingPruneDaysEnqueue(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "pending_prune_days_enqueue_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testPendingPruneDays(t), }, @@ -34,7 +34,7 @@ func TestPendingPruneDaysEnqueue(t *testing.T) { func TestPendingPruneDaysEnqueue_Truflation(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "pending_prune_days_enqueue_truflation_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testPendingPruneDaysTruflation(t), }, diff --git a/tests/streams/primitive_batch_insert_alignment_test.go b/tests/streams/primitive_batch_insert_alignment_test.go index a0a710750..5883f3da7 100644 --- a/tests/streams/primitive_batch_insert_alignment_test.go +++ b/tests/streams/primitive_batch_insert_alignment_test.go @@ -32,7 +32,7 @@ import ( func TestPrimitiveBatchInsertAlignment(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "primitive_batch_insert_alignment_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testBatchAlignment(t), }, diff --git a/tests/streams/primitive_test.go b/tests/streams/primitive_test.go index 6adb7f68b..bcf1caeff 100644 --- a/tests/streams/primitive_test.go +++ b/tests/streams/primitive_test.go @@ -23,7 +23,7 @@ var primitiveStreamId = util.GenerateStreamId(primitiveStreamName) func TestPrimitiveStream(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "primitive_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testPRIMITIVE01_DataInsertion(t), }, diff --git a/tests/streams/query/metadata_test.go b/tests/streams/query/metadata_test.go index 745ab0a04..baaa092d0 100644 --- a/tests/streams/query/metadata_test.go +++ b/tests/streams/query/metadata_test.go @@ -46,7 +46,7 @@ func TestQUERY04Metadata(t *testing.T) { WithMetadataTestSetup(testListMetadataByHeightInvalidRange(t, primitiveContractInfo)), WithMetadataTestSetup(testListMetadataByHeightInvalidPagination(t, primitiveContractInfo)), }, - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), }, testutils.GetTestOptionsWithCache()) } diff --git a/tests/streams/query/query_test.go b/tests/streams/query/query_test.go index 01244e69d..1994842ea 100644 --- a/tests/streams/query/query_test.go +++ b/tests/streams/query/query_test.go @@ -52,7 +52,7 @@ var primitiveChildStreamId = util.GenerateStreamId(primitiveChildStreamName) func TestQueryStream(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "query_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ WithQueryTestSetup(testQUERY01_InsertAndGetRecord(t)), WithQueryTestSetup(testQUERY06_GetRecordWithFutureDate(t)), diff --git a/tests/streams/roles/permission_gates_test.go b/tests/streams/roles/permission_gates_test.go index 94a5fdfbd..35ba69664 100644 --- a/tests/streams/roles/permission_gates_test.go +++ b/tests/streams/roles/permission_gates_test.go @@ -17,8 +17,8 @@ import ( func TestPermissionGates(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ - Name: "permission_gates_test", - SeedScripts: migrations.GetSeedScriptPaths(), + Name: "permission_gates_test", + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ func(ctx context.Context, platform *kwilTesting.Platform) error { testStreamCreationPermissionGates(t, ctx, platform) @@ -29,6 +29,14 @@ func TestPermissionGates(t *testing.T) { } func testStreamCreationPermissionGates(t *testing.T, ctx context.Context, platform *kwilTesting.Platform) { + // NOTE: With transaction fees implementation, the system:network_writer role changed from + // a permission gate to a fee exemption gate: + // - Users WITH role: Can create streams without fees (same as before) + // - Users WITHOUT role: Can create streams but must pay 2 TRUF per stream (breaking change) + // + // This test verifies that unauthorized users get "Insufficient balance" errors (not "permission denied") + // since they can create streams if they pay fees, but these test addresses have zero balance. + // Initialize platform deployer with a valid address defaultDeployer := util.Unsafe_NewEthereumAddressFromString("0x0000000000000000000000000000000000000000") platform.Deployer = defaultDeployer.Bytes() @@ -65,7 +73,7 @@ func testStreamCreationPermissionGates(t *testing.T, ctx context.Context, platfo testCases := []permissionTestCase{ { - name: "unauthorized user cannot create single stream", + name: "unauthorized user cannot create single stream without paying fee", platform: unauthorizedPlatform, userAddress: unauthorizedUser, action: func(p *kwilTesting.Platform, user string) error { @@ -73,10 +81,10 @@ func testStreamCreationPermissionGates(t *testing.T, ctx context.Context, platfo return setup.UntypedCreateStream(ctx, p, streamID.String(), user, "primitive") }, expectSuccess: false, - expectedError: "Caller does not have the required system:network_writer role", + expectedError: "Insufficient balance for stream creation", }, { - name: "unauthorized user cannot create multiple streams", + name: "unauthorized user cannot create multiple streams without paying fee", platform: unauthorizedPlatform, userAddress: unauthorizedUser, action: func(p *kwilTesting.Platform, user string) error { @@ -85,7 +93,7 @@ func testStreamCreationPermissionGates(t *testing.T, ctx context.Context, platfo }) }, expectSuccess: false, - expectedError: "Caller does not have the required system:network_writer role", + expectedError: "Insufficient balance for stream creation", // Changed: non-role-members must pay fees }, { name: "authorized user can create single stream", @@ -121,7 +129,7 @@ func testStreamCreationPermissionGates(t *testing.T, ctx context.Context, platfo }) } - t.Run("permission gates respond to role state changes", func(t *testing.T) { + t.Run("fee exemption responds to role state changes", func(t *testing.T) { // Revoke the role err := procedure.RevokeRoles(ctx, procedure.RevokeRolesInput{ Platform: managerPlatform, @@ -131,10 +139,10 @@ func testStreamCreationPermissionGates(t *testing.T, ctx context.Context, platfo }) require.NoError(t, err) - // Now verify the formerly authorized user cannot create a stream + // Now verify the formerly authorized user must pay fees (insufficient balance error) streamID := util.GenerateStreamId("post_revocation") err = setup.UntypedCreateStream(ctx, authorizedPlatform, streamID.String(), authorizedWriter, "primitive") - require.ErrorContains(t, err, "Caller does not have the required system:network_writer role", "User should not be able to create stream after role is revoked") + require.ErrorContains(t, err, "Insufficient balance for stream creation", "User should be charged fees after role is revoked") // Re-grant the role err = procedure.GrantRoles(ctx, procedure.GrantRolesInput{ @@ -145,10 +153,10 @@ func testStreamCreationPermissionGates(t *testing.T, ctx context.Context, platfo }) require.NoError(t, err) - // Verify the user can create streams again + // Verify the user can create streams without fees after role is re-granted streamID2 := util.GenerateStreamId("post_regrant") err = setup.UntypedCreateStream(ctx, authorizedPlatform, streamID2.String(), authorizedWriter, "primitive") - require.NoError(t, err, "User should be able to create stream after role is re-granted") + require.NoError(t, err, "User should be able to create stream without fees after role is re-granted") }) } diff --git a/tests/streams/roles/role_management_test.go b/tests/streams/roles/role_management_test.go index a0d33b8b1..360e1d71b 100644 --- a/tests/streams/roles/role_management_test.go +++ b/tests/streams/roles/role_management_test.go @@ -83,7 +83,7 @@ func TestRoleManagementSuite(t *testing.T) { t.Run(tc.name, func(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: tc.name, - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ func(ctx context.Context, platform *kwilTesting.Platform) error { // Initialize platform deployer with a valid address to prevent errors in procedure calls. diff --git a/tests/streams/stream_creation_fee_test.go b/tests/streams/stream_creation_fee_test.go new file mode 100644 index 000000000..a9a73fe52 --- /dev/null +++ b/tests/streams/stream_creation_fee_test.go @@ -0,0 +1,453 @@ +//go:build kwiltest + +package tests + +import ( + "context" + "fmt" + "math/big" + "testing" + + "github.com/stretchr/testify/require" + "github.com/trufnetwork/kwil-db/common" + "github.com/trufnetwork/kwil-db/core/crypto" + coreauth "github.com/trufnetwork/kwil-db/core/crypto/auth" + kwilTesting "github.com/trufnetwork/kwil-db/testing" + "github.com/trufnetwork/node/internal/migrations" + testutils "github.com/trufnetwork/node/tests/streams/utils" + testerc20 "github.com/trufnetwork/node/tests/streams/utils/erc20" + "github.com/trufnetwork/node/tests/streams/utils/setup" + "github.com/trufnetwork/sdk-go/core/util" +) + +// Test constants - must match erc20-bridge/000-extension.sql configuration +const ( + testChain = "sepolia" + testEscrow = "0x502430eD0BbE0f230215870c9C2853e126eE5Ae3" // From erc20-bridge/000-extension.sql + testERC20 = "0x2222222222222222222222222222222222222222" + testExtensionName = "sepolia_bridge" + feeAmount = "2000000000000000000" // 2 TRUF with 18 decimals +) + +var ( + twoTRUF = mustParseBigInt(feeAmount) // 2 TRUF as big.Int + pointCounter int64 = 10 // Start from 10, increment for each balance injection +) + +func mustParseBigInt(s string) *big.Int { + val := new(big.Int) + val.SetString(s, 10) + return val +} + +// TestStreamCreationFees is the main test suite for stream creation transaction fees +func TestStreamCreationFees(t *testing.T) { + testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ + Name: "STREAM_FEE01_StreamCreationFees", + SeedStatements: migrations.GetSeedScriptStatements(), + FunctionTests: []kwilTesting.TestFunc{ + setupTestEnvironment(t), + testExemptWalletNoFee(t), + testNonExemptWalletPaysFee(t), + testInsufficientBalance(t), + testRoleChangeAffectsFee(t), + testBatchCreationPerStreamFee(t), + testLeaderReceivesFees(t), + }, + }, testutils.GetTestOptionsWithCache()) +} + +// setupTestEnvironment grants network_writer role to a default test address (like Taskfile.yml does) +// Note: ERC20 bridge is already initialized by 0_test_only/bootstrap_erc20.sql +// +// Data Provider Creation Scenarios in Tests: +// 1. setup.CreateDataProvider() - Whitelisted (with network_writer role) - NO FEES +// 2. setup.CreateDataProviderWithoutRole() - Non-whitelisted (without role) - PAYS FEES +func setupTestEnvironment(t *testing.T) func(ctx context.Context, platform *kwilTesting.Platform) error { + return func(ctx context.Context, platform *kwilTesting.Platform) error { + // Use the system admin address (derived from private key 0x00...01) + // This address has permission to manage system roles, just like in Taskfile.yml + systemAdmin := util.Unsafe_NewEthereumAddressFromString("0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf") + platform.Deployer = systemAdmin.Bytes() + + // Grant network_writers_manager role so system admin can manage network_writer roles + // This allows the test to use procedure.RevokeRoles() without needing bypass + err := setup.AddMemberToRoleBypass(ctx, platform, "system", "network_writers_manager", systemAdmin.Address()) + if err != nil { + return fmt.Errorf("failed to grant network_writers_manager to system admin: %w", err) + } + + // Also grant network_writer role to system admin (like Taskfile: grant_roles text:system text:network_writer text[]:{{.DEV_DB_OWNER}}) + // This is needed so test infrastructure can create streams for setup + err = setup.AddMemberToRoleBypass(ctx, platform, "system", "network_writer", systemAdmin.Address()) + if err != nil { + return fmt.Errorf("failed to grant network_writer to system admin: %w", err) + } + + return nil + } +} + +// Test 1: Exempt wallet creates stream without paying fee +func testExemptWalletNoFee(t *testing.T) func(ctx context.Context, platform *kwilTesting.Platform) error { + return func(ctx context.Context, platform *kwilTesting.Platform) error { + exemptAddrVal := util.Unsafe_NewEthereumAddressFromString("0x1111111111111111111111111111111111111111") + exemptAddr := &exemptAddrVal + + // Register data provider (this also grants network_writer role and registers in data_providers table) + err := setup.CreateDataProvider(ctx, platform, exemptAddr.Address()) + require.NoError(t, err, "failed to create data provider") + + // Give balance to verify it doesn't change + err = giveBalance(ctx, platform, exemptAddr.Address(), "100000000000000000000") + require.NoError(t, err, "failed to give balance") + + // Get initial balance + initialBalance, err := getBalance(ctx, platform, exemptAddr.Address()) + require.NoError(t, err, "failed to get initial balance") + + // Create stream as exempt user + err = createStream(ctx, platform, exemptAddr, "st000000000000000000000000000001", "primitive") + require.NoError(t, err, "stream creation should succeed for exempt wallet") + + // Verify balance unchanged + finalBalance, err := getBalance(ctx, platform, exemptAddr.Address()) + require.NoError(t, err, "failed to get final balance") + require.Equal(t, initialBalance, finalBalance, "Balance should not change for exempt wallet") + + return nil + } +} + +// Test 2: Non-exempt wallet pays 2 TRUF fee +func testNonExemptWalletPaysFee(t *testing.T) func(ctx context.Context, platform *kwilTesting.Platform) error { + return func(ctx context.Context, platform *kwilTesting.Platform) error { + userAddrVal := util.Unsafe_NewEthereumAddressFromString("0x3333333333333333333333333333333333333333") + userAddr := &userAddrVal + + // Register data provider WITHOUT role (non-whitelisted, will pay fees) + err := setup.CreateDataProviderWithoutRole(ctx, platform, userAddr.Address()) + require.NoError(t, err, "failed to register data provider") + + // Give user 100 TRUF + err = giveBalance(ctx, platform, userAddr.Address(), "100000000000000000000") + require.NoError(t, err, "failed to give balance") + + // Get initial balance + initialBalance, err := getBalance(ctx, platform, userAddr.Address()) + require.NoError(t, err, "failed to get initial balance") + + // Create stream + err = createStream(ctx, platform, userAddr, "st000000000000000000000000000002", "primitive") + require.NoError(t, err, "stream creation should succeed") + + // Verify balance decreased by 2 TRUF + finalBalance, err := getBalance(ctx, platform, userAddr.Address()) + require.NoError(t, err, "failed to get final balance") + + expectedBalance := new(big.Int).Sub(initialBalance, twoTRUF) + require.Equal(t, 0, expectedBalance.Cmp(finalBalance), + "Balance should decrease by 2 TRUF, expected %s but got %s", expectedBalance, finalBalance) + + return nil + } +} + +// Test 3: Insufficient balance fails +func testInsufficientBalance(t *testing.T) func(ctx context.Context, platform *kwilTesting.Platform) error { + return func(ctx context.Context, platform *kwilTesting.Platform) error { + userAddrVal := util.Unsafe_NewEthereumAddressFromString("0x4444444444444444444444444444444444444444") + userAddr := &userAddrVal + + // Register data provider WITHOUT role (non-whitelisted, will pay fees) + err := setup.CreateDataProviderWithoutRole(ctx, platform, userAddr.Address()) + require.NoError(t, err, "failed to register data provider") + + // Give user only 1 TRUF (insufficient) + err = giveBalance(ctx, platform, userAddr.Address(), "1000000000000000000") + require.NoError(t, err, "failed to give balance") + + // Try to create stream (should fail) + err = createStream(ctx, platform, userAddr, "st000000000000000000000000000003", "primitive") + require.Error(t, err, "stream creation should fail with insufficient balance") + require.Contains(t, err.Error(), "Insufficient balance for stream creation", + "error should mention insufficient balance") + + return nil + } +} + +// Test 4: Role change affects fee behavior +func testRoleChangeAffectsFee(t *testing.T) func(ctx context.Context, platform *kwilTesting.Platform) error { + return func(ctx context.Context, platform *kwilTesting.Platform) error { + userAddrVal := util.Unsafe_NewEthereumAddressFromString("0x5555555555555555555555555555555555555555") + userAddr := &userAddrVal + + // Register data provider WITHOUT role (non-whitelisted, will pay fees) + err := setup.CreateDataProviderWithoutRole(ctx, platform, userAddr.Address()) + require.NoError(t, err, "failed to register data provider") + + // Give user 100 TRUF + err = giveBalance(ctx, platform, userAddr.Address(), "100000000000000000000") + require.NoError(t, err, "failed to give balance") + + // First call: User pays fee (not exempt) + initialBalance, err := getBalance(ctx, platform, userAddr.Address()) + require.NoError(t, err) + + err = createStream(ctx, platform, userAddr, "st000000000000000000000000000004", "primitive") + require.NoError(t, err, "first stream creation should succeed") + + balanceAfterFirst, err := getBalance(ctx, platform, userAddr.Address()) + require.NoError(t, err) + + expectedAfterFirst := new(big.Int).Sub(initialBalance, twoTRUF) + require.Equal(t, 0, expectedAfterFirst.Cmp(balanceAfterFirst), "First call should charge 2 TRUF fee") + + // Grant role + err = setup.AddMemberToRoleBypass(ctx, platform, "system", "network_writer", userAddr.Address()) + require.NoError(t, err, "failed to grant role") + + // Second call: User is now exempt + err = createStream(ctx, platform, userAddr, "st000000000000000000000000000005", "primitive") + require.NoError(t, err, "second stream creation should succeed") + + balanceAfterSecond, err := getBalance(ctx, platform, userAddr.Address()) + require.NoError(t, err) + require.Equal(t, 0, balanceAfterFirst.Cmp(balanceAfterSecond), "Second call should not charge fee (now exempt)") + + // Revoke role using bypass (same pattern as AddMemberToRoleBypass) + err = revokeRoleBypass(ctx, platform, "system", "network_writer", userAddr.Address()) + require.NoError(t, err, "failed to revoke role") + + // Third call: User pays fee again + err = createStream(ctx, platform, userAddr, "st000000000000000000000000000006", "primitive") + require.NoError(t, err, "third stream creation should succeed") + + balanceAfterThird, err := getBalance(ctx, platform, userAddr.Address()) + require.NoError(t, err) + + expectedAfterThird := new(big.Int).Sub(balanceAfterSecond, twoTRUF) + require.Equal(t, 0, expectedAfterThird.Cmp(balanceAfterThird), "Third call should charge fee (role revoked)") + + return nil + } +} + +// Test 5: Batch stream creation charges fee per stream (not per call) +func testBatchCreationPerStreamFee(t *testing.T) func(ctx context.Context, platform *kwilTesting.Platform) error { + return func(ctx context.Context, platform *kwilTesting.Platform) error { + userAddrVal := util.Unsafe_NewEthereumAddressFromString("0x6666666666666666666666666666666666666666") + userAddr := &userAddrVal + + // Register data provider WITHOUT role (non-whitelisted, will pay fees) + err := setup.CreateDataProviderWithoutRole(ctx, platform, userAddr.Address()) + require.NoError(t, err, "failed to register data provider") + + // Give user 100 TRUF + err = giveBalance(ctx, platform, userAddr.Address(), "100000000000000000000") + require.NoError(t, err, "failed to give balance") + + // Get initial balance + initialBalance, err := getBalance(ctx, platform, userAddr.Address()) + require.NoError(t, err) + + // Create 3 streams in batch + streamIds := []string{ + "st000000000000000000000000000007", + "st000000000000000000000000000008", + "st000000000000000000000000000009", + } + streamTypes := []string{"primitive", "primitive", "primitive"} + + err = createStreams(ctx, platform, userAddr, streamIds, streamTypes) + require.NoError(t, err, "batch stream creation should succeed") + + // Verify balance decreased by 6 TRUF (2 TRUF × 3 streams) + finalBalance, err := getBalance(ctx, platform, userAddr.Address()) + require.NoError(t, err) + + numStreams := int64(len(streamIds)) // 3 streams + expectedFee := new(big.Int).Mul(twoTRUF, big.NewInt(numStreams)) + expectedBalance := new(big.Int).Sub(initialBalance, expectedFee) + require.Equal(t, 0, expectedBalance.Cmp(finalBalance), + "Batch should charge 2 TRUF per stream (3 streams = 6 TRUF), expected %s but got %s", expectedBalance, finalBalance) + + return nil + } +} + +// Test 6: Leader receives fees +func testLeaderReceivesFees(t *testing.T) func(ctx context.Context, platform *kwilTesting.Platform) error { + return func(ctx context.Context, platform *kwilTesting.Platform) error { + userAddrVal := util.Unsafe_NewEthereumAddressFromString("0x7777777777777777777777777777777777777777") + userAddr := &userAddrVal + + // Register data provider WITHOUT role (non-whitelisted, will pay fees) + err := setup.CreateDataProviderWithoutRole(ctx, platform, userAddr.Address()) + require.NoError(t, err, "failed to register data provider") + + // Setup leader + _, pubGeneric, err := crypto.GenerateSecp256k1Key(nil) + require.NoError(t, err, "failed to generate leader key") + pub := pubGeneric.(*crypto.Secp256k1PublicKey) + leaderSigner := crypto.EthereumAddressFromPubKey(pub) + leaderAddr := fmt.Sprintf("0x%x", leaderSigner) + + // Give user 100 TRUF + err = giveBalance(ctx, platform, userAddr.Address(), "100000000000000000000") + require.NoError(t, err, "failed to give user balance") + + // Give leader initial balance (so we can track the increase) + err = giveBalance(ctx, platform, leaderAddr, "10000000000000000000") + require.NoError(t, err, "failed to give leader balance") + + // Get initial leader balance + initialLeaderBalance, err := getBalance(ctx, platform, leaderAddr) + require.NoError(t, err, "failed to get initial leader balance") + + // Create stream with specific leader + err = createStreamWithLeader(ctx, platform, userAddr, pub, "st00000000000000000000000000000a", "primitive") + require.NoError(t, err, "stream creation with leader should succeed") + + // Verify leader balance increased by 2 TRUF + finalLeaderBalance, err := getBalance(ctx, platform, leaderAddr) + require.NoError(t, err, "failed to get final leader balance") + + expectedLeaderBalance := new(big.Int).Add(initialLeaderBalance, twoTRUF) + require.Equal(t, 0, expectedLeaderBalance.Cmp(finalLeaderBalance), + "Leader should receive 2 TRUF fee, expected %s but got %s", expectedLeaderBalance, finalLeaderBalance) + + return nil + } +} + +// ===== HELPER FUNCTIONS ===== + +// revokeRoleBypass revokes a role using direct SQL with OverrideAuthz +// This mirrors the pattern used by setup.AddMemberToRoleBypass() +// way to manage roles in tests without complex authorization setup +func revokeRoleBypass(ctx context.Context, platform *kwilTesting.Platform, owner, roleName, wallet string) error { + txContext := &common.TxContext{ + Ctx: ctx, + BlockContext: &common.BlockContext{Height: 0}, + TxID: platform.Txid(), + Signer: []byte("system"), + Caller: "0x0000000000000000000000000000000000000000", + } + + engineContext := &common.EngineContext{ + TxContext: txContext, + OverrideAuthz: true, // Skip authorization checks (test utility pattern) + } + + sql := `DELETE FROM role_members WHERE owner = $owner AND role_name = $role_name AND wallet = $wallet` + + err := platform.Engine.Execute(engineContext, platform.DB, sql, map[string]any{ + "$owner": owner, + "$role_name": roleName, + "$wallet": wallet, + }, func(row *common.Row) error { + return nil + }) + if err != nil { + return fmt.Errorf("failed to revoke role: %w", err) + } + + return nil +} + +// giveBalance credits TRUF balance to a wallet using ERC20 inject +func giveBalance(ctx context.Context, platform *kwilTesting.Platform, wallet string, amountStr string) error { + pointCounter++ // Use unique point for each injection + return testerc20.InjectERC20Transfer( + ctx, + platform, + testChain, + testEscrow, + testERC20, + wallet, + wallet, + amountStr, + pointCounter, + nil, + ) +} + +// getBalance retrieves the TRUF balance for a wallet +func getBalance(ctx context.Context, platform *kwilTesting.Platform, wallet string) (*big.Int, error) { + balanceStr, err := testerc20.GetUserBalance(ctx, platform, testExtensionName, wallet) + if err != nil { + return nil, err + } + + balance := new(big.Int) + if _, ok := balance.SetString(balanceStr, 10); !ok { + return nil, fmt.Errorf("invalid balance string: %s", balanceStr) + } + + return balance, nil +} + +// callCreateStreamsAction is the base implementation - calls the create_streams action +func callCreateStreamsAction(ctx context.Context, platform *kwilTesting.Platform, signer *util.EthereumAddress, leaderPub *crypto.Secp256k1PublicKey, streamIds []string, streamTypes []string) error { + tx := &common.TxContext{ + Ctx: ctx, + BlockContext: &common.BlockContext{ + Height: 1, + Proposer: leaderPub, + }, + Signer: signer.Bytes(), + Caller: signer.Address(), + TxID: platform.Txid(), + Authenticator: coreauth.EthPersonalSignAuth, + } + engineCtx := &common.EngineContext{TxContext: tx} + + res, err := platform.Engine.Call( + engineCtx, + platform.DB, + "", + "create_streams", + []any{streamIds, streamTypes}, + func(row *common.Row) error { return nil }, + ) + if err != nil { + return err + } + if res != nil && res.Error != nil { + return res.Error + } + return nil +} + +// Convenience wrappers that call callCreateStreamsAction with specific parameters + +// createStream creates a single stream with a randomly generated leader +func createStream(ctx context.Context, platform *kwilTesting.Platform, signer *util.EthereumAddress, streamId string, streamType string) error { + // Generate random leader + _, pubGeneric, err := crypto.GenerateSecp256k1Key(nil) + if err != nil { + return err + } + pub := pubGeneric.(*crypto.Secp256k1PublicKey) + + return callCreateStreamsAction(ctx, platform, signer, pub, []string{streamId}, []string{streamType}) +} + +// createStreams creates multiple streams with a randomly generated leader +func createStreams(ctx context.Context, platform *kwilTesting.Platform, signer *util.EthereumAddress, streamIds []string, streamTypes []string) error { + // Generate random leader + _, pubGeneric, err := crypto.GenerateSecp256k1Key(nil) + if err != nil { + return err + } + pub := pubGeneric.(*crypto.Secp256k1PublicKey) + + return callCreateStreamsAction(ctx, platform, signer, pub, streamIds, streamTypes) +} + +// createStreamWithLeader creates a single stream with a specific leader (for testing fee recipient) +func createStreamWithLeader(ctx context.Context, platform *kwilTesting.Platform, signer *util.EthereumAddress, leaderPub *crypto.Secp256k1PublicKey, streamId string, streamType string) error { + return callCreateStreamsAction(ctx, platform, signer, leaderPub, []string{streamId}, []string{streamType}) +} diff --git a/tests/streams/taxonomy_query_actions_test.go b/tests/streams/taxonomy_query_actions_test.go index 8bfccac1b..b676a9524 100644 --- a/tests/streams/taxonomy_query_actions_test.go +++ b/tests/streams/taxonomy_query_actions_test.go @@ -33,7 +33,7 @@ const ( func TestTaxonomyQueryActions(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "taxonomy_query_actions_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ testListTaxonomiesByHeight(t), testListTaxonomiesByHeightWithLatestOnly(t), diff --git a/tests/streams/truflation_composed_frozen_test.go b/tests/streams/truflation_composed_frozen_test.go index 7872f1a73..f158b856d 100644 --- a/tests/streams/truflation_composed_frozen_test.go +++ b/tests/streams/truflation_composed_frozen_test.go @@ -31,7 +31,7 @@ var ( func TestTruflationComposedFrozen(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "truflation_composed_frozen_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ setupTruflationFrozenComposedTest(testTruflationComposed1(t)), setupTruflationFrozenComposedTest(testTruflationComposed2(t)), diff --git a/tests/streams/truflation_primitive_frozen_test.go b/tests/streams/truflation_primitive_frozen_test.go index e63bb1c26..b8660b187 100644 --- a/tests/streams/truflation_primitive_frozen_test.go +++ b/tests/streams/truflation_primitive_frozen_test.go @@ -26,7 +26,7 @@ var ( func TestTruflationFrozen(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ Name: "truflation_frozen_test", - SeedScripts: migrations.GetSeedScriptPaths(), + SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ setupTruflationFrozenTest(testAllBeforeFrozen(t)), setupTruflationFrozenTest(testAllAfterFrozen(t)), diff --git a/tests/streams/utils/setup/common.go b/tests/streams/utils/setup/common.go index 62fc6ac9f..f1e023eeb 100644 --- a/tests/streams/utils/setup/common.go +++ b/tests/streams/utils/setup/common.go @@ -2,6 +2,7 @@ package setup import ( "context" + "strings" "github.com/pkg/errors" "github.com/trufnetwork/kwil-db/common" @@ -232,6 +233,107 @@ func CreateDataProvider(ctx context.Context, platform *kwilTesting.Platform, add return nil } +// CreateDataProviderWithoutRole registers a data provider WITHOUT granting the network_writer role. +// This is useful for testing fee collection scenarios where the data provider should pay fees. +// +// Note: This function: +// 1. Temporarily grants network_writer role to register the provider (required by create_data_provider action) +// 2. Immediately revokes the role after registration +// 3. Leaves the data provider registered but non-whitelisted (will pay fees) +func CreateDataProviderWithoutRole(ctx context.Context, platform *kwilTesting.Platform, address string) error { + addr, err := util.NewEthereumAddressFromString(address) + if err != nil { + return errors.Wrap(err, "invalid data provider address") + } + + // First, grant role temporarily (required to call create_data_provider) + err = AddMemberToRoleBypass(ctx, platform, "system", "network_writer", addr.Address()) + if err != nil { + return errors.Wrap(err, "failed to grant temporary network_writer role") + } + + // Register the data provider + txContext := &common.TxContext{ + Ctx: ctx, + BlockContext: &common.BlockContext{Height: 1}, + Signer: addr.Bytes(), + Caller: addr.Address(), + TxID: platform.Txid(), + } + + engineContext := &common.EngineContext{ + TxContext: txContext, + } + + r, err := platform.Engine.Call(engineContext, + platform.DB, + "", + "create_data_provider", + []any{addr.Address()}, + func(row *common.Row) error { + return nil + }, + ) + if err != nil { + // Clean up: revoke the temporary role before returning error + _ = removeMemberFromRoleBypass(ctx, platform, "system", "network_writer", addr.Address()) + return errors.Wrap(err, "error in create_data_provider") + } + if r.Error != nil { + // Clean up: revoke the temporary role before returning error + _ = removeMemberFromRoleBypass(ctx, platform, "system", "network_writer", addr.Address()) + return errors.Wrap(r.Error, "error in create_data_provider") + } + + // Now revoke the role so they have to pay fees + err = removeMemberFromRoleBypass(ctx, platform, "system", "network_writer", addr.Address()) + if err != nil { + return errors.Wrap(err, "failed to revoke network_writer role") + } + + return nil +} + +// removeMemberFromRoleBypass revokes a role using direct SQL with OverrideAuthz. +// This mirrors AddMemberToRoleBypass and uses direct SQL instead of calling revoke_roles +// action because: +// 1. Calling revoke_roles requires authorization (caller must be role owner or manager) +// 2. Test setup doesn't guarantee proper authorization context +// 3. This is a test utility following the same pattern as AddMemberToRoleBypass +// 4. Using OverrideAuthz is the standard pattern for test role management +func removeMemberFromRoleBypass(ctx context.Context, platform *kwilTesting.Platform, owner, roleName, wallet string) error { + txContext := &common.TxContext{ + Ctx: ctx, + BlockContext: &common.BlockContext{Height: 0}, + TxID: platform.Txid(), + Signer: []byte("system"), + Caller: "0x0000000000000000000000000000000000000000", + } + + engineContext := &common.EngineContext{ + TxContext: txContext, + OverrideAuthz: true, // Skip authorization checks - this is a test utility + } + + // Direct SQL DELETE is idempotent - deleting a non-existent role membership is a no-op + sql := `DELETE FROM role_members WHERE owner = $owner AND role_name = $role_name AND wallet = $wallet` + + // Normalize to lowercase to match AddMemberToRoleBypass behavior + // (role_members table stores lowercase values, checksummed addresses won't match otherwise) + err := platform.Engine.Execute(engineContext, platform.DB, sql, map[string]any{ + "$owner": strings.ToLower(owner), + "$role_name": strings.ToLower(roleName), + "$wallet": strings.ToLower(wallet), + }, func(row *common.Row) error { + return nil + }) + if err != nil { + return errors.Wrap(err, "failed to remove role member") + } + + return nil +} + // GetStreamId resolves a stream reference using the data provider address and stream ID // This uses the get_stream_id action from the database to dynamically resolve stream refs // instead of hardcoding them as "streamRef := 1"