v / vlib / orm / orm_generic_struct_field_test.v
178 lines · 143 sloc · 4.03 KB · 83d8f86e23cf52b14f67b6bbcc803cc2d3841e9f
Raw
1// vtest build: present_sqlite3? && !windows && !sanitize-memory-clang
2// vtest retry: 3
3// Tests for generic structs where the type parameter is used as a field type
4import db.sqlite
5
6// Test struct that will be used as a generic type parameter
7struct Payload {
8 some_field_1 string
9 some_field_2 int
10}
11
12// Generic struct with a field of type T - this requires proper ORM configuration
13// The payload field will be treated as a foreign key reference
14pub struct Message[T] {
15 id int @[primary; sql: serial]
16 data string
17 payload T @[fkey: 'id']
18}
19
20// Payload needs to be a proper ORM table with a primary key
21struct PayloadTable {
22 id int @[primary; sql: serial]
23 some_field_1 string
24 some_field_2 int
25}
26
27// Generic struct with proper ORM configuration
28pub struct MessageWithPayload[T] {
29 id int @[primary; sql: serial]
30 data string
31 payload T @[fkey: 'id']
32}
33
34fn test_generic_struct_with_struct_field_and_primary_key() {
35 mut db := sqlite.connect(':memory:')!
36
37 // Create tables - PayloadTable first since it's referenced
38 sql db {
39 create table PayloadTable
40 }!
41
42 // Insert a payload
43 payload := PayloadTable{
44 some_field_1: 'test'
45 some_field_2: 42
46 }
47 sql db {
48 insert payload into PayloadTable
49 }!
50
51 // Verify the payload was inserted
52 payloads := sql db {
53 select from PayloadTable
54 }!
55 assert payloads.len == 1
56 assert payloads[0].some_field_1 == 'test'
57 assert payloads[0].some_field_2 == 42
58
59 db.close()!
60}
61
62// Test that generic structs with simple fields work correctly
63pub struct SimpleMessage[T] {
64 id int @[primary; sql: serial]
65 data string
66 status int
67}
68
69fn test_generic_struct_with_simple_fields() {
70 mut db := sqlite.connect(':memory:')!
71
72 sql db {
73 create table SimpleMessage[Payload]
74 }!
75
76 // The table should be created successfully
77 // Note: SimpleMessage[Payload] doesn't actually use Payload as a field type,
78 // so this works fine
79
80 db.close()!
81}
82
83// Test that skipping struct fields with @[sql: '-'] works
84pub struct MessageWithSkippedField[T] {
85 id int @[primary; sql: serial]
86 data string
87 payload T @[sql: '-'] // Skip this field in ORM
88}
89
90fn test_generic_struct_with_skipped_field() {
91 mut db := sqlite.connect(':memory:')!
92
93 // This should work because the payload field is skipped
94 sql db {
95 create table MessageWithSkippedField[Payload]
96 }!
97
98 db.close()!
99}
100
101// Test generic struct creation inside a generic function
102// This tests the fix for issue #26433
103pub struct GenericMessage[T] {
104 id int @[primary; sql: serial]
105 data string
106 payload T @[sql: '-'] // Skipped to avoid needing fkey setup
107}
108
109pub struct Queue[T] {
110pub mut:
111 conn &sqlite.DB
112}
113
114pub fn create_queue[T](path string) !Queue[T] {
115 mut conn := sqlite.connect(path)!
116
117 mut queue := Queue[T]{
118 conn: &conn
119 }
120
121 // This should work: creating a table with a generic struct inside a generic function
122 sql queue.conn {
123 create table GenericMessage[T]
124 }!
125
126 return queue
127}
128
129fn test_create_table_in_generic_function() {
130 mut queue := create_queue[Payload](':memory:')!
131
132 // Verify the table was created by inserting and selecting data
133 msg := GenericMessage[Payload]{
134 data: 'test message'
135 }
136 sql queue.conn {
137 insert msg into GenericMessage[Payload]
138 }!
139
140 messages := sql queue.conn {
141 select from GenericMessage[Payload]
142 }!
143
144 assert messages.len == 1
145 assert messages[0].data == 'test message'
146
147 queue.conn.close()!
148}
149
150// Test inserting from within a generic function
151// This tests the fix for the "cannot use `Message` as `Message[Payload]`" error
152pub fn (mut q Queue[T]) add_message(data string) !GenericMessage[T] {
153 msg := GenericMessage[T]{
154 data: data
155 }
156 sql q.conn {
157 insert msg into GenericMessage[T]
158 }!
159 return msg
160}
161
162fn test_insert_in_generic_function() {
163 mut queue := create_queue[Payload](':memory:')!
164
165 // Insert from within a generic function
166 msg := queue.add_message('inserted from generic fn')!
167 assert msg.data == 'inserted from generic fn'
168
169 // Verify it was inserted
170 messages := sql queue.conn {
171 select from GenericMessage[Payload]
172 }!
173
174 assert messages.len == 1
175 assert messages[0].data == 'inserted from generic fn'
176
177 queue.conn.close()!
178}
179