a database layer insipred by caqti and ecto
1open Repodb
2
3let test_sql_type_name_int () =
4 Alcotest.(check string)
5 "int maps to INTEGER" "INTEGER"
6 (Types.sql_type_name Types.int)
7
8let test_sql_type_name_int64 () =
9 Alcotest.(check string)
10 "int64 maps to BIGINT" "BIGINT"
11 (Types.sql_type_name Types.int64)
12
13let test_sql_type_name_string () =
14 Alcotest.(check string)
15 "string maps to TEXT" "TEXT"
16 (Types.sql_type_name Types.string)
17
18let test_sql_type_name_bool () =
19 Alcotest.(check string)
20 "bool maps to BOOLEAN" "BOOLEAN"
21 (Types.sql_type_name Types.bool)
22
23let test_sql_type_name_float () =
24 Alcotest.(check string)
25 "float maps to DOUBLE PRECISION" "DOUBLE PRECISION"
26 (Types.sql_type_name Types.float)
27
28let test_sql_type_name_ptime () =
29 Alcotest.(check string)
30 "ptime maps to TIMESTAMPTZ" "TIMESTAMPTZ"
31 (Types.sql_type_name Types.ptime)
32
33let test_sql_type_name_pdate () =
34 Alcotest.(check string)
35 "pdate maps to DATE" "DATE"
36 (Types.sql_type_name Types.pdate)
37
38let test_sql_type_name_pdate_sqlite () =
39 Alcotest.(check string)
40 "pdate maps to TEXT for SQLite" "TEXT"
41 (Types.sql_type_name_for_sqlite Types.pdate)
42
43let test_sql_type_name_uuid () =
44 Alcotest.(check string)
45 "uuid maps to UUID" "UUID"
46 (Types.sql_type_name Types.uuid)
47
48let test_sql_type_name_json () =
49 Alcotest.(check string)
50 "json maps to JSONB" "JSONB"
51 (Types.sql_type_name Types.json)
52
53let test_sql_type_name_option () =
54 Alcotest.(check string)
55 "option string still maps to TEXT" "TEXT"
56 (Types.sql_type_name (Types.option Types.string))
57
58let test_sql_type_name_array () =
59 Alcotest.(check string)
60 "array int maps to INTEGER[]" "INTEGER[]"
61 (Types.sql_type_name (Types.array Types.int))
62
63let test_is_nullable_option () =
64 Alcotest.(check bool)
65 "option is nullable" true
66 (Types.is_nullable (Types.option Types.string))
67
68let test_is_nullable_non_option () =
69 Alcotest.(check bool)
70 "non-option is not nullable" false
71 (Types.is_nullable Types.string)
72
73let test_custom_type () =
74 let custom =
75 Types.custom
76 ~encode:(fun s -> Ok s)
77 ~decode:(fun s -> Ok s)
78 ~sql_type:"CUSTOM_TYPE"
79 in
80 Alcotest.(check string)
81 "custom type name" "CUSTOM_TYPE"
82 (Types.sql_type_name custom)
83
84let test_pdate_to_value () =
85 let date : Ptime.date = (2024, 1, 15) in
86 let value = Types.to_value Types.pdate date in
87 match value with
88 | Driver.Value.Text s ->
89 Alcotest.(check string) "date serializes to YYYY-MM-DD" "2024-01-15" s
90 | _ -> Alcotest.fail "expected Text value"
91
92let test_pdate_to_value_single_digit_month_day () =
93 let date : Ptime.date = (2024, 3, 5) in
94 let value = Types.to_value Types.pdate date in
95 match value with
96 | Driver.Value.Text s ->
97 Alcotest.(check string) "single digit month/day padded" "2024-03-05" s
98 | _ -> Alcotest.fail "expected Text value"
99
100let test_pdate_of_value () =
101 let value = Driver.Value.Text "2024-01-15" in
102 match Types.of_value Types.pdate value with
103 | Ok (y, m, d) ->
104 Alcotest.(check int) "year" 2024 y;
105 Alcotest.(check int) "month" 1 m;
106 Alcotest.(check int) "day" 15 d
107 | Error e -> Alcotest.fail ("failed to parse date: " ^ e)
108
109let test_pdate_of_value_unpadded () =
110 let value = Driver.Value.Text "2024-3-5" in
111 match Types.of_value Types.pdate value with
112 | Ok (y, m, d) ->
113 Alcotest.(check int) "year" 2024 y;
114 Alcotest.(check int) "month" 3 m;
115 Alcotest.(check int) "day" 5 d
116 | Error e -> Alcotest.fail ("failed to parse date: " ^ e)
117
118let test_pdate_roundtrip () =
119 let original : Ptime.date = (2023, 12, 31) in
120 let value = Types.to_value Types.pdate original in
121 match Types.of_value Types.pdate value with
122 | Ok result ->
123 Alcotest.(check (triple int int int))
124 "roundtrip preserves date" original result
125 | Error e -> Alcotest.fail ("roundtrip failed: " ^ e)
126
127let test_pdate_of_value_invalid () =
128 let value = Driver.Value.Text "not-a-date" in
129 match Types.of_value Types.pdate value with
130 | Ok _ -> Alcotest.fail "should have failed on invalid date"
131 | Error _ -> ()
132
133let test_pdate_of_value_null () =
134 let value = Driver.Value.Null in
135 match Types.of_value Types.pdate value with
136 | Ok _ -> Alcotest.fail "should have failed on NULL"
137 | Error _ -> ()
138
139let test_pdate_option_null () =
140 let value = Driver.Value.Null in
141 match Types.of_value (Types.option Types.pdate) value with
142 | Ok None -> ()
143 | Ok (Some _) -> Alcotest.fail "expected None for NULL"
144 | Error e -> Alcotest.fail ("unexpected error: " ^ e)
145
146let test_pdate_option_some () =
147 let value = Driver.Value.Text "2024-06-15" in
148 match Types.of_value (Types.option Types.pdate) value with
149 | Ok (Some (y, m, d)) ->
150 Alcotest.(check int) "year" 2024 y;
151 Alcotest.(check int) "month" 6 m;
152 Alcotest.(check int) "day" 15 d
153 | Ok None -> Alcotest.fail "expected Some date"
154 | Error e -> Alcotest.fail ("unexpected error: " ^ e)
155
156let tests =
157 [
158 ("sql_type_name int", `Quick, test_sql_type_name_int);
159 ("sql_type_name int64", `Quick, test_sql_type_name_int64);
160 ("sql_type_name string", `Quick, test_sql_type_name_string);
161 ("sql_type_name bool", `Quick, test_sql_type_name_bool);
162 ("sql_type_name float", `Quick, test_sql_type_name_float);
163 ("sql_type_name ptime", `Quick, test_sql_type_name_ptime);
164 ("sql_type_name pdate", `Quick, test_sql_type_name_pdate);
165 ("sql_type_name pdate sqlite", `Quick, test_sql_type_name_pdate_sqlite);
166 ("sql_type_name uuid", `Quick, test_sql_type_name_uuid);
167 ("sql_type_name json", `Quick, test_sql_type_name_json);
168 ("sql_type_name option", `Quick, test_sql_type_name_option);
169 ("sql_type_name array", `Quick, test_sql_type_name_array);
170 ("is_nullable option", `Quick, test_is_nullable_option);
171 ("is_nullable non-option", `Quick, test_is_nullable_non_option);
172 ("custom type", `Quick, test_custom_type);
173 ("pdate to_value", `Quick, test_pdate_to_value);
174 ("pdate to_value padded", `Quick, test_pdate_to_value_single_digit_month_day);
175 ("pdate of_value", `Quick, test_pdate_of_value);
176 ("pdate of_value unpadded", `Quick, test_pdate_of_value_unpadded);
177 ("pdate roundtrip", `Quick, test_pdate_roundtrip);
178 ("pdate of_value invalid", `Quick, test_pdate_of_value_invalid);
179 ("pdate of_value null", `Quick, test_pdate_of_value_null);
180 ("pdate option null", `Quick, test_pdate_option_null);
181 ("pdate option some", `Quick, test_pdate_option_some);
182 ]