11use std:: ffi:: CStr ;
2+ use std:: collections:: HashSet ;
23
34use sqlite_wasm_rs:: {
45 sqlite3, sqlite3_close, sqlite3_column_text, sqlite3_finalize, sqlite3_open_v2,
56 sqlite3_prepare_v3, sqlite3_step, SQLITE_OK , SQLITE_OPEN_CREATE , SQLITE_OPEN_READWRITE ,
67 SQLITE_ROW ,
78} ;
89use wasm_bindgen:: prelude:: wasm_bindgen;
10+ use uuid:: { Uuid , Version } ;
911
1012#[ wasm_bindgen]
1113extern "C" {
@@ -17,8 +19,36 @@ macro_rules! console_log {
1719 ( $( $t: tt) * ) => ( log( & format_args!( $( $t) * ) . to_string( ) ) )
1820}
1921
22+ unsafe fn query_uuid ( db : * mut sqlite3 , sql_stmt : * const std:: ffi:: c_char ) -> String {
23+ let mut stmt = std:: ptr:: null_mut ( ) ;
24+ let rc = sqlite3_prepare_v3 (
25+ db,
26+ sql_stmt,
27+ -1 ,
28+ 0 ,
29+ & mut stmt,
30+ std:: ptr:: null_mut ( ) ,
31+ ) ;
32+ assert_eq ! ( SQLITE_OK , rc, "Failed to prepare statement" ) ;
33+
34+ let mut res = String :: new ( ) ;
35+ if sqlite3_step ( stmt) == SQLITE_ROW {
36+ let val_ptr = sqlite3_column_text ( stmt, 0 ) ;
37+ if !val_ptr. is_null ( ) {
38+ res = CStr :: from_ptr ( val_ptr. cast ( ) ) . to_str ( ) . unwrap ( ) . to_string ( ) ;
39+ } else {
40+ panic ! ( "Returned NULL for UUID query" ) ;
41+ }
42+ } else {
43+ panic ! ( "No row returned for UUID query" ) ;
44+ }
45+ sqlite3_finalize ( stmt) ;
46+ res
47+ }
48+
2049#[ wasm_bindgen( start) ]
2150async fn main ( ) {
51+ console_error_panic_hook:: set_once ( ) ;
2252 let mut db: * mut sqlite3 = std:: ptr:: null_mut ( ) ;
2353 // Open in-memory DB
2454 let ret = unsafe {
@@ -30,112 +60,94 @@ async fn main() {
3060 )
3161 } ;
3262 assert_eq ! ( SQLITE_OK , ret) ;
33- console_log ! ( "db opened: {db:?} " ) ;
63+ console_log ! ( "db opened" ) ;
3464
3565 unsafe {
36- // Register uuid4 extension
66+ // Register extensions
3767 let rc = sqlite_wasm_uuid4:: register ( db. cast ( ) ) ;
3868 assert_eq ! ( SQLITE_OK , rc) ;
3969 console_log ! ( "UUID4 extension registered" ) ;
4070
41- // Register uuid7 extension
4271 let rc = sqlite_wasm_uuid7:: register ( db. cast ( ) ) ;
4372 assert_eq ! ( SQLITE_OK , rc) ;
4473 console_log ! ( "UUID7 extension registered" ) ;
4574
46- // Test uuid()
47- console_log ! ( "Testing SELECT uuid();" ) ;
75+ // --- UUID4 Test ---
76+ console_log ! ( "Generating 1000 UUIDv4..." ) ;
77+ let mut v4_results = Vec :: with_capacity ( 1000 ) ;
4878 let sql = c"SELECT uuid();" ;
49- let mut stmt = std:: ptr:: null_mut ( ) ;
50- let rc = sqlite3_prepare_v3 (
51- db,
52- sql. as_ptr ( ) . cast ( ) ,
53- -1 ,
54- 0 ,
55- & mut stmt,
56- std:: ptr:: null_mut ( ) ,
57- ) ;
58- assert_eq ! ( SQLITE_OK , rc) ;
59-
60- if sqlite3_step ( stmt) == SQLITE_ROW {
61- let val_ptr = sqlite3_column_text ( stmt, 0 ) ;
62- if !val_ptr. is_null ( ) {
63- let s = CStr :: from_ptr ( val_ptr. cast ( ) ) . to_str ( ) . unwrap ( ) ;
64- console_log ! ( "uuid() result: {}" , s) ;
65-
66- // Validate format: 8-4-4-4-12 hex digits
67- assert_eq ! ( s. len( ) , 36 ) ;
68- assert_eq ! ( s. chars( ) . nth( 8 ) , Some ( '-' ) ) ;
69- assert_eq ! ( s. chars( ) . nth( 13 ) , Some ( '-' ) ) ;
70- assert_eq ! ( s. chars( ) . nth( 18 ) , Some ( '-' ) ) ;
71- assert_eq ! ( s. chars( ) . nth( 23 ) , Some ( '-' ) ) ;
72- } else {
73- console_log ! ( "uuid() returned NULL" ) ;
74- }
75- } else {
76- console_log ! ( "Error: No row returned for uuid()" ) ;
79+
80+ for _ in 0 ..1000 {
81+ v4_results. push ( query_uuid ( db, sql. as_ptr ( ) ) ) ;
7782 }
78- sqlite3_finalize ( stmt) ;
79-
80- // Test uuid_str()
81- console_log ! ( "Testing SELECT uuid_str(uuid_blob('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'));" ) ;
82- // We know 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11' is a valid uuid
83- let sql = c"SELECT uuid_str(uuid_blob('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'));" ;
84- let rc = sqlite3_prepare_v3 (
85- db,
86- sql. as_ptr ( ) . cast ( ) ,
87- -1 ,
88- 0 ,
89- & mut stmt,
90- std:: ptr:: null_mut ( ) ,
91- ) ;
92- assert_eq ! ( SQLITE_OK , rc) ;
9383
94- if sqlite3_step ( stmt) == SQLITE_ROW {
95- let val_ptr = sqlite3_column_text ( stmt, 0 ) ;
96- if !val_ptr. is_null ( ) {
97- let s = CStr :: from_ptr ( val_ptr. cast ( ) ) . to_str ( ) . unwrap ( ) ;
98- console_log ! ( "uuid_str(...) result: {}" , s) ;
99- assert_eq ! ( s, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11" ) ;
100- } else {
101- console_log ! ( "uuid_str(...) returned NULL" ) ;
102- }
103- } else {
104- console_log ! ( "Error: No row returned for uuid_str()" ) ;
84+ // Check uniqueness
85+ let set: HashSet < _ > = v4_results. iter ( ) . cloned ( ) . collect ( ) ;
86+ assert_eq ! ( set. len( ) , 1000 , "All UUIDv4 must be unique" ) ;
87+ console_log ! ( "Unique check passed for UUIDv4" ) ;
88+
89+ // Check version & parsing
90+ for ( i, s) in v4_results. iter ( ) . enumerate ( ) {
91+ let u = Uuid :: parse_str ( s) . unwrap_or_else ( |e| panic ! ( "Failed to parse UUIDv4 at index {}: {} - {}" , i, s, e) ) ;
92+ assert_eq ! ( u. get_version( ) , Some ( Version :: Random ) , "UUID at index {} is not v4: {}" , i, s) ;
10593 }
106- sqlite3_finalize ( stmt ) ;
94+ console_log ! ( "Version check passed for UUIDv4" ) ;
10795
108- // Test uuid7()
109- console_log ! ( "Testing SELECT uuid7();" ) ;
96+ // --- UUID7 Test ---
97+ console_log ! ( "Generating 1000 UUIDv7..." ) ;
98+ let mut v7_results = Vec :: with_capacity ( 1000 ) ;
11099 let sql = c"SELECT uuid7();" ;
111- let mut stmt = std:: ptr:: null_mut ( ) ;
112- let rc = sqlite3_prepare_v3 (
113- db,
114- sql. as_ptr ( ) . cast ( ) ,
115- -1 ,
116- 0 ,
117- & mut stmt,
118- std:: ptr:: null_mut ( ) ,
119- ) ;
120- assert_eq ! ( SQLITE_OK , rc) ;
121100
122- if sqlite3_step ( stmt) == SQLITE_ROW {
123- let val_ptr = sqlite3_column_text ( stmt, 0 ) ;
124- if !val_ptr. is_null ( ) {
125- let s = CStr :: from_ptr ( val_ptr. cast ( ) ) . to_str ( ) . unwrap ( ) ;
126- console_log ! ( "uuid7() result: {}" , s) ;
127- assert_eq ! ( s. len( ) , 36 ) ;
128- assert_eq ! ( s. chars( ) . nth( 14 ) , Some ( '7' ) ) ;
129- } else {
130- console_log ! ( "uuid7() returned NULL" ) ;
131- }
132- } else {
133- console_log ! ( "Error: No row returned for uuid7()" ) ;
101+ for _ in 0 ..1000 {
102+ v7_results. push ( query_uuid ( db, sql. as_ptr ( ) ) ) ;
134103 }
135- sqlite3_finalize ( stmt) ;
136104
105+ // Check uniqueness
106+ let set: HashSet < _ > = v7_results. iter ( ) . cloned ( ) . collect ( ) ;
107+ assert_eq ! ( set. len( ) , 1000 , "All UUIDv7 must be unique" ) ;
108+ console_log ! ( "Unique check passed for UUIDv7" ) ;
109+
110+ // Check sorting (Monotonicity)
111+ // Since UUIDv7 is time-ordered, later generations should generally be greater than previous ones.
112+ // However, if generated in the same millisecond, the random component decides order.
113+ // Strict monotonicity isn't guaranteed across all implementations unless they share state,
114+ // but uuid::Uuid::now_v7() generally attempts internally to be monotonic if called rapidly.
115+ // Wait, the Rust `uuid` crate documentation says `now_v7` guarantees monotonicity for same-process calls if the feature is enabled?
116+ // Actually `Uuid::now_v7()` uses `context::Context` thread-locally if available, or just random.
117+ // The implementation in extensions/uuid7 uses `Uuid::now_v7()`.
118+ // Let's check if the list is sorted.
119+
120+ let mut sorted_v7 = v7_results. clone ( ) ;
121+ sorted_v7. sort ( ) ;
122+
123+ // This assertion might be flaky if the clock goes backwards or multiple threads (not in WASM mostly).
124+ // But for a single thread tight loop, it should be strictly monotonic or at least non-decreasing.
125+ // Since we assert uniqueness, non-decreasing + unique = strictly increasing.
126+
127+ if v7_results != sorted_v7 {
128+ console_log ! ( "Warning: UUIDv7 results were not strictly monotonic." ) ;
129+ // Find first violation
130+ for i in 0 ..999 {
131+ if v7_results[ i] > v7_results[ i+1 ] {
132+ console_log ! ( "Violation at index {}: {} > {}" , i, v7_results[ i] , v7_results[ i+1 ] ) ;
133+ // Allow slight harmless reordering if any, but UUIDv7 SHOULD be monotonic.
134+ // The user asked to "check they are unique and sorted".
135+ // So I will assert it.
136+ }
137+ }
138+ panic ! ( "UUIDv7 results are not sorted!" ) ;
139+ }
140+ console_log ! ( "Sort order check passed for UUIDv7" ) ;
141+
142+ // Check version
143+ for ( i, s) in v7_results. iter ( ) . enumerate ( ) {
144+ let u = Uuid :: parse_str ( s) . unwrap_or_else ( |e| panic ! ( "Failed to parse UUIDv7 at index {}: {} - {}" , i, s, e) ) ;
145+ assert_eq ! ( u. get_version( ) , Some ( Version :: SortRand ) , "UUID at index {} is not v7: {}" , i, s) ;
146+ }
147+ console_log ! ( "Version check passed for UUIDv7" ) ;
148+
137149 sqlite3_close ( db) ;
138150 }
139-
151+
140152 console_log ! ( "All tests passed!" ) ;
141153}
0 commit comments