- Deprecated/broken migration files archived - New auth_schema migration (015) - Trading service events migration (016) - Migration renumbering utility - Migration test suite
453 lines
14 KiB
PL/PgSQL
453 lines
14 KiB
PL/PgSQL
-- ================================================================================================
|
|
-- Migration Test Suite: Trading Events (Migration 001)
|
|
-- PostgreSQL 16.10 + TimescaleDB 2.22.1
|
|
-- Tests constraint violations, valid insertions, partitioning, and index usage
|
|
-- ================================================================================================
|
|
|
|
-- Setup test transaction (rollback at end to avoid polluting database)
|
|
BEGIN;
|
|
|
|
-- ================================================================================================
|
|
-- TEST 1: Valid Trading Event Insertion
|
|
-- Should succeed with all required fields
|
|
-- ================================================================================================
|
|
DO $$
|
|
DECLARE
|
|
v_test_id UUID := uuid_generate_v4();
|
|
v_correlation_id UUID := uuid_generate_v4();
|
|
v_current_ns BIGINT := EXTRACT(EPOCH FROM NOW())::BIGINT * 1000000000;
|
|
BEGIN
|
|
INSERT INTO trading_events (
|
|
id,
|
|
correlation_id,
|
|
event_timestamp,
|
|
received_timestamp,
|
|
processing_timestamp,
|
|
event_type,
|
|
event_source,
|
|
symbol,
|
|
event_data,
|
|
event_hash,
|
|
node_id,
|
|
process_id
|
|
) VALUES (
|
|
v_test_id,
|
|
v_correlation_id,
|
|
v_current_ns,
|
|
v_current_ns + 1000,
|
|
v_current_ns + 2000,
|
|
'order_submitted',
|
|
'trading_engine',
|
|
'AAPL',
|
|
'{"order_id": "TEST001", "price": 150.00, "quantity": 100}'::jsonb,
|
|
encode(sha256('test_data'::bytea), 'hex'),
|
|
'node-01',
|
|
12345
|
|
);
|
|
|
|
RAISE NOTICE 'TEST 1 PASS: Valid trading event inserted successfully';
|
|
EXCEPTION
|
|
WHEN OTHERS THEN
|
|
RAISE EXCEPTION 'TEST 1 FAIL: Valid insertion failed - %', SQLERRM;
|
|
END $$;
|
|
|
|
-- ================================================================================================
|
|
-- TEST 2: Constraint Violation - Invalid ns_timestamp (negative value)
|
|
-- Should fail with CHECK constraint violation
|
|
-- ================================================================================================
|
|
DO $$
|
|
BEGIN
|
|
INSERT INTO trading_events (
|
|
correlation_id,
|
|
event_timestamp,
|
|
received_timestamp,
|
|
processing_timestamp,
|
|
event_type,
|
|
event_source,
|
|
symbol,
|
|
event_data,
|
|
event_hash,
|
|
node_id,
|
|
process_id
|
|
) VALUES (
|
|
uuid_generate_v4(),
|
|
-1000, -- INVALID: negative timestamp
|
|
1000000000,
|
|
1000000000,
|
|
'order_submitted',
|
|
'trading_engine',
|
|
'AAPL',
|
|
'{}'::jsonb,
|
|
encode(sha256('test'::bytea), 'hex'),
|
|
'node-01',
|
|
12345
|
|
);
|
|
|
|
RAISE EXCEPTION 'TEST 2 FAIL: Should have rejected negative timestamp';
|
|
EXCEPTION
|
|
WHEN check_violation THEN
|
|
RAISE NOTICE 'TEST 2 PASS: Correctly rejected negative ns_timestamp';
|
|
WHEN OTHERS THEN
|
|
RAISE EXCEPTION 'TEST 2 FAIL: Wrong error type - %', SQLERRM;
|
|
END $$;
|
|
|
|
-- ================================================================================================
|
|
-- TEST 3: Constraint Violation - Missing NOT NULL field
|
|
-- Should fail with NOT NULL constraint violation
|
|
-- ================================================================================================
|
|
DO $$
|
|
BEGIN
|
|
INSERT INTO trading_events (
|
|
correlation_id,
|
|
event_timestamp,
|
|
received_timestamp,
|
|
processing_timestamp,
|
|
event_type,
|
|
event_source,
|
|
-- symbol is missing (NOT NULL constraint)
|
|
event_data,
|
|
event_hash,
|
|
node_id,
|
|
process_id
|
|
) VALUES (
|
|
uuid_generate_v4(),
|
|
1000000000,
|
|
1000000000,
|
|
1000000000,
|
|
'order_submitted',
|
|
'trading_engine',
|
|
'{}'::jsonb,
|
|
encode(sha256('test'::bytea), 'hex'),
|
|
'node-01',
|
|
12345
|
|
);
|
|
|
|
RAISE EXCEPTION 'TEST 3 FAIL: Should have rejected missing symbol';
|
|
EXCEPTION
|
|
WHEN not_null_violation THEN
|
|
RAISE NOTICE 'TEST 3 PASS: Correctly rejected NULL symbol';
|
|
WHEN OTHERS THEN
|
|
RAISE EXCEPTION 'TEST 3 FAIL: Wrong error type - %', SQLERRM;
|
|
END $$;
|
|
|
|
-- ================================================================================================
|
|
-- TEST 4: Enum Constraint Violation - Invalid event_type
|
|
-- Should fail with invalid input for enum type
|
|
-- ================================================================================================
|
|
DO $$
|
|
BEGIN
|
|
INSERT INTO trading_events (
|
|
correlation_id,
|
|
event_timestamp,
|
|
received_timestamp,
|
|
processing_timestamp,
|
|
event_type,
|
|
event_source,
|
|
symbol,
|
|
event_data,
|
|
event_hash,
|
|
node_id,
|
|
process_id
|
|
) VALUES (
|
|
uuid_generate_v4(),
|
|
1000000000,
|
|
1000000000,
|
|
1000000000,
|
|
'invalid_event_type', -- INVALID: not in enum
|
|
'trading_engine',
|
|
'AAPL',
|
|
'{}'::jsonb,
|
|
encode(sha256('test'::bytea), 'hex'),
|
|
'node-01',
|
|
12345
|
|
);
|
|
|
|
RAISE EXCEPTION 'TEST 4 FAIL: Should have rejected invalid event_type';
|
|
EXCEPTION
|
|
WHEN invalid_text_representation THEN
|
|
RAISE NOTICE 'TEST 4 PASS: Correctly rejected invalid event_type enum value';
|
|
WHEN OTHERS THEN
|
|
RAISE EXCEPTION 'TEST 4 FAIL: Wrong error type - %', SQLERRM;
|
|
END $$;
|
|
|
|
-- ================================================================================================
|
|
-- TEST 5: Timestamp Ordering Constraint
|
|
-- Processing timestamp should be >= received timestamp >= event timestamp
|
|
-- ================================================================================================
|
|
DO $$
|
|
DECLARE
|
|
v_current_ns BIGINT := EXTRACT(EPOCH FROM NOW())::BIGINT * 1000000000;
|
|
BEGIN
|
|
-- Should fail: received < event
|
|
INSERT INTO trading_events (
|
|
correlation_id,
|
|
event_timestamp,
|
|
received_timestamp,
|
|
processing_timestamp,
|
|
event_type,
|
|
event_source,
|
|
symbol,
|
|
event_data,
|
|
event_hash,
|
|
node_id,
|
|
process_id
|
|
) VALUES (
|
|
uuid_generate_v4(),
|
|
v_current_ns,
|
|
v_current_ns - 1000, -- INVALID: received before event
|
|
v_current_ns,
|
|
'order_submitted',
|
|
'trading_engine',
|
|
'AAPL',
|
|
'{}'::jsonb,
|
|
encode(sha256('test'::bytea), 'hex'),
|
|
'node-01',
|
|
12345
|
|
);
|
|
|
|
RAISE EXCEPTION 'TEST 5 FAIL: Should have rejected invalid timestamp ordering';
|
|
EXCEPTION
|
|
WHEN check_violation THEN
|
|
RAISE NOTICE 'TEST 5 PASS: Correctly rejected invalid timestamp ordering';
|
|
WHEN OTHERS THEN
|
|
RAISE EXCEPTION 'TEST 5 FAIL: Wrong error type - %', SQLERRM;
|
|
END $$;
|
|
|
|
-- ================================================================================================
|
|
-- TEST 6: Partition Routing Test
|
|
-- Verify events are routed to correct partitions by date
|
|
-- ================================================================================================
|
|
DO $$
|
|
DECLARE
|
|
v_test_id UUID := uuid_generate_v4();
|
|
v_correlation_id UUID := uuid_generate_v4();
|
|
v_current_ns BIGINT := EXTRACT(EPOCH FROM NOW())::BIGINT * 1000000000;
|
|
v_partition_count INTEGER;
|
|
v_expected_date DATE;
|
|
BEGIN
|
|
-- Insert event
|
|
INSERT INTO trading_events (
|
|
id,
|
|
correlation_id,
|
|
event_timestamp,
|
|
received_timestamp,
|
|
processing_timestamp,
|
|
event_type,
|
|
event_source,
|
|
symbol,
|
|
event_data,
|
|
event_hash,
|
|
node_id,
|
|
process_id
|
|
) VALUES (
|
|
v_test_id,
|
|
v_correlation_id,
|
|
v_current_ns,
|
|
v_current_ns,
|
|
v_current_ns,
|
|
'order_submitted',
|
|
'trading_engine',
|
|
'AAPL',
|
|
'{"test": "partition_routing"}'::jsonb,
|
|
encode(sha256('partition_test'::bytea), 'hex'),
|
|
'node-01',
|
|
12345
|
|
);
|
|
|
|
-- Verify event_date was auto-populated
|
|
SELECT event_date INTO v_expected_date
|
|
FROM trading_events
|
|
WHERE id = v_test_id;
|
|
|
|
IF v_expected_date = CURRENT_DATE THEN
|
|
RAISE NOTICE 'TEST 6 PASS: Event routed to correct partition (event_date = %)', v_expected_date;
|
|
ELSE
|
|
RAISE EXCEPTION 'TEST 6 FAIL: Event_date mismatch (expected: %, got: %)', CURRENT_DATE, v_expected_date;
|
|
END IF;
|
|
|
|
-- Check partition exists
|
|
SELECT COUNT(*) INTO v_partition_count
|
|
FROM pg_tables
|
|
WHERE tablename LIKE 'trading_events_%'
|
|
AND tablename = 'trading_events_' || to_char(CURRENT_DATE, 'YYYY_MM_DD');
|
|
|
|
IF v_partition_count > 0 THEN
|
|
RAISE NOTICE 'TEST 6 INFO: Partition exists for today (trading_events_%)', to_char(CURRENT_DATE, 'YYYY_MM_DD');
|
|
ELSE
|
|
RAISE NOTICE 'TEST 6 WARNING: Expected partition not found';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ================================================================================================
|
|
-- TEST 7: Index Usage Verification - Symbol Index
|
|
-- Verify index is used for symbol queries (EXPLAIN ANALYZE)
|
|
-- ================================================================================================
|
|
DO $$
|
|
DECLARE
|
|
v_plan_row RECORD;
|
|
v_uses_index BOOLEAN := FALSE;
|
|
BEGIN
|
|
-- Check query plan for index usage
|
|
FOR v_plan_row IN
|
|
EXPLAIN (FORMAT TEXT)
|
|
SELECT * FROM trading_events WHERE symbol = 'AAPL' LIMIT 100
|
|
LOOP
|
|
IF v_plan_row."QUERY PLAN" LIKE '%Index%' OR v_plan_row."QUERY PLAN" LIKE '%Bitmap%' THEN
|
|
v_uses_index := TRUE;
|
|
RAISE NOTICE 'TEST 7 PASS: Index detected - %', v_plan_row."QUERY PLAN";
|
|
END IF;
|
|
END LOOP;
|
|
|
|
IF NOT v_uses_index THEN
|
|
RAISE NOTICE 'TEST 7 WARNING: No index scan detected (may be seq scan for small tables)';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ================================================================================================
|
|
-- TEST 8: JSONB Operations - Event Data Query
|
|
-- Verify JSONB indexing and query performance
|
|
-- ================================================================================================
|
|
DO $$
|
|
DECLARE
|
|
v_test_id UUID := uuid_generate_v4();
|
|
v_correlation_id UUID := uuid_generate_v4();
|
|
v_current_ns BIGINT := EXTRACT(EPOCH FROM NOW())::BIGINT * 1000000000;
|
|
v_result JSONB;
|
|
BEGIN
|
|
-- Insert event with complex JSONB
|
|
INSERT INTO trading_events (
|
|
id,
|
|
correlation_id,
|
|
event_timestamp,
|
|
received_timestamp,
|
|
processing_timestamp,
|
|
event_type,
|
|
event_source,
|
|
symbol,
|
|
event_data,
|
|
event_hash,
|
|
node_id,
|
|
process_id
|
|
) VALUES (
|
|
v_test_id,
|
|
v_correlation_id,
|
|
v_current_ns,
|
|
v_current_ns,
|
|
v_current_ns,
|
|
'order_submitted',
|
|
'trading_engine',
|
|
'AAPL',
|
|
'{"order_id": "TEST008", "price": 150.50, "quantity": 1000, "metadata": {"source": "algo"}}'::jsonb,
|
|
encode(sha256('jsonb_test'::bytea), 'hex'),
|
|
'node-01',
|
|
12345
|
|
);
|
|
|
|
-- Query using JSONB operators
|
|
SELECT event_data INTO v_result
|
|
FROM trading_events
|
|
WHERE id = v_test_id
|
|
AND event_data->>'order_id' = 'TEST008';
|
|
|
|
IF v_result IS NOT NULL THEN
|
|
RAISE NOTICE 'TEST 8 PASS: JSONB query successful, retrieved: %', v_result;
|
|
ELSE
|
|
RAISE EXCEPTION 'TEST 8 FAIL: JSONB query failed';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ================================================================================================
|
|
-- TEST 9: Order Lifecycle Validation
|
|
-- Test complete order workflow: submit -> accept -> fill
|
|
-- ================================================================================================
|
|
DO $$
|
|
DECLARE
|
|
v_order_id UUID := uuid_generate_v4();
|
|
v_current_ns BIGINT := EXTRACT(EPOCH FROM NOW())::BIGINT * 1000000000;
|
|
BEGIN
|
|
-- Create order
|
|
INSERT INTO orders (
|
|
id, symbol, side, order_type, time_in_force,
|
|
quantity, limit_price, account_id, venue
|
|
) VALUES (
|
|
v_order_id, 'AAPL', 'buy', 'limit', 'day',
|
|
100, 15000, 'ACC001', 'NASDAQ'
|
|
);
|
|
|
|
-- Verify order was created
|
|
IF EXISTS (SELECT 1 FROM orders WHERE id = v_order_id AND status = 'pending') THEN
|
|
RAISE NOTICE 'TEST 9 PASS: Order created with pending status';
|
|
ELSE
|
|
RAISE EXCEPTION 'TEST 9 FAIL: Order creation failed';
|
|
END IF;
|
|
|
|
-- Verify trading event was auto-generated by trigger
|
|
IF EXISTS (SELECT 1 FROM trading_events WHERE correlation_id = v_order_id AND event_type = 'order_submitted') THEN
|
|
RAISE NOTICE 'TEST 9 PASS: Order submission event auto-generated';
|
|
ELSE
|
|
RAISE EXCEPTION 'TEST 9 FAIL: Order event not generated';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ================================================================================================
|
|
-- TEST 10: Performance - Bulk Insert
|
|
-- Verify performance of bulk insertions
|
|
-- ================================================================================================
|
|
DO $$
|
|
DECLARE
|
|
v_start_time TIMESTAMP;
|
|
v_end_time TIMESTAMP;
|
|
v_duration INTERVAL;
|
|
v_correlation_id UUID := uuid_generate_v4();
|
|
v_current_ns BIGINT := EXTRACT(EPOCH FROM NOW())::BIGINT * 1000000000;
|
|
i INTEGER;
|
|
BEGIN
|
|
v_start_time := clock_timestamp();
|
|
|
|
-- Insert 100 events
|
|
FOR i IN 1..100 LOOP
|
|
INSERT INTO trading_events (
|
|
correlation_id,
|
|
event_timestamp,
|
|
received_timestamp,
|
|
processing_timestamp,
|
|
event_type,
|
|
event_source,
|
|
symbol,
|
|
event_data,
|
|
event_hash,
|
|
node_id,
|
|
process_id
|
|
) VALUES (
|
|
v_correlation_id,
|
|
v_current_ns + i,
|
|
v_current_ns + i,
|
|
v_current_ns + i,
|
|
'order_submitted',
|
|
'trading_engine',
|
|
'BULK' || (i % 10),
|
|
format('{"order_id": "BULK%s"}', i)::jsonb,
|
|
encode(sha256(('bulk' || i)::bytea), 'hex'),
|
|
'node-01',
|
|
12345
|
|
);
|
|
END LOOP;
|
|
|
|
v_end_time := clock_timestamp();
|
|
v_duration := v_end_time - v_start_time;
|
|
|
|
RAISE NOTICE 'TEST 10 PASS: Bulk insert of 100 events completed in %', v_duration;
|
|
|
|
IF v_duration > INTERVAL '1 second' THEN
|
|
RAISE WARNING 'TEST 10 WARNING: Bulk insert took > 1 second, may indicate performance issue';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ================================================================================================
|
|
-- Cleanup: Rollback all test data
|
|
-- ================================================================================================
|
|
ROLLBACK;
|
|
|
|
-- Test Summary
|
|
SELECT 'Trading Events Test Suite Completed - Review NOTICE messages above for results' AS test_summary;
|