1+ use crate :: test_fixtures:: cube_bridge:: {
2+ MockDimensionDefinition , MockMeasureDefinition , MockSchema , MockSchemaBuilder ,
3+ MockSegmentDefinition ,
4+ } ;
5+
6+ /// Creates a schema for visitors and visitor_checkins cubes
7+ ///
8+ /// This schema demonstrates:
9+ /// - Basic dimensions with different types
10+ /// - Geo dimensions with latitude/longitude
11+ /// - Sub-query dimensions that reference other cubes
12+ /// - Dimensions with complex SQL including special characters (question marks)
13+ /// - Time dimensions
14+ pub fn create_visitors_schema ( ) -> MockSchema {
15+ MockSchemaBuilder :: new ( )
16+ // visitor_checkins cube - referenced by visitors cube
17+ . add_cube ( "visitor_checkins" )
18+ . add_dimension (
19+ "id" ,
20+ MockDimensionDefinition :: builder ( )
21+ . dimension_type ( "number" . to_string ( ) )
22+ . sql ( "id" . to_string ( ) )
23+ . build ( ) ,
24+ )
25+ . add_dimension (
26+ "visitor_id" ,
27+ MockDimensionDefinition :: builder ( )
28+ . dimension_type ( "number" . to_string ( ) )
29+ . sql ( "visitor_id" . to_string ( ) )
30+ . build ( ) ,
31+ )
32+ . add_dimension (
33+ "minDate" ,
34+ MockDimensionDefinition :: builder ( )
35+ . dimension_type ( "time" . to_string ( ) )
36+ . sql ( "MIN(created_at)" . to_string ( ) )
37+ . build ( ) ,
38+ )
39+ . add_dimension (
40+ "minDate1" ,
41+ MockDimensionDefinition :: builder ( )
42+ . dimension_type ( "time" . to_string ( ) )
43+ . sql ( "MIN(created_at) + INTERVAL '1 day'" . to_string ( ) )
44+ . build ( ) ,
45+ )
46+ . add_measure (
47+ "count" ,
48+ MockMeasureDefinition :: builder ( )
49+ . measure_type ( "count" . to_string ( ) )
50+ . sql ( "COUNT(*)" . to_string ( ) )
51+ . build ( ) ,
52+ )
53+ . finish_cube ( )
54+ // visitors cube - main cube with various dimension types
55+ . add_cube ( "visitors" )
56+ . add_dimension (
57+ "id" ,
58+ MockDimensionDefinition :: builder ( )
59+ . dimension_type ( "number" . to_string ( ) )
60+ . sql ( "id" . to_string ( ) )
61+ . build ( ) ,
62+ )
63+ . add_dimension (
64+ "visitor_id" ,
65+ MockDimensionDefinition :: builder ( )
66+ . dimension_type ( "number" . to_string ( ) )
67+ . sql ( "visitor_id" . to_string ( ) )
68+ . build ( ) ,
69+ )
70+ . add_dimension (
71+ "source" ,
72+ MockDimensionDefinition :: builder ( )
73+ . dimension_type ( "string" . to_string ( ) )
74+ . sql ( "source" . to_string ( ) )
75+ . build ( ) ,
76+ )
77+ . add_dimension (
78+ "created_at" ,
79+ MockDimensionDefinition :: builder ( )
80+ . dimension_type ( "time" . to_string ( ) )
81+ . sql ( "created_at" . to_string ( ) )
82+ . build ( ) ,
83+ )
84+ // Sub-query dimension referencing visitor_checkins.minDate
85+ . add_dimension (
86+ "minVisitorCheckinDate" ,
87+ MockDimensionDefinition :: builder ( )
88+ . dimension_type ( "time" . to_string ( ) )
89+ . sql ( "{visitor_checkins.minDate}" . to_string ( ) )
90+ . sub_query ( Some ( true ) )
91+ . build ( ) ,
92+ )
93+ // Sub-query dimension referencing visitor_checkins.minDate1
94+ . add_dimension (
95+ "minVisitorCheckinDate1" ,
96+ MockDimensionDefinition :: builder ( )
97+ . dimension_type ( "time" . to_string ( ) )
98+ . sql ( "{visitor_checkins.minDate1}" . to_string ( ) )
99+ . sub_query ( Some ( true ) )
100+ . build ( ) ,
101+ )
102+ // Geo dimension with latitude and longitude
103+ . add_dimension (
104+ "location" ,
105+ MockDimensionDefinition :: builder ( )
106+ . dimension_type ( "geo" . to_string ( ) )
107+ . latitude ( "latitude" . to_string ( ) )
108+ . longitude ( "longitude" . to_string ( ) )
109+ . build ( ) ,
110+ )
111+ // Dimension with SQL containing question marks (special characters)
112+ . add_dimension (
113+ "questionMark" ,
114+ MockDimensionDefinition :: builder ( )
115+ . dimension_type ( "string" . to_string ( ) )
116+ . sql (
117+ "replace('some string question string ? ?? ???', 'string', 'with some ? ?? ???')"
118+ . to_string ( ) ,
119+ )
120+ . build ( ) ,
121+ )
122+ . add_measure (
123+ "count" ,
124+ MockMeasureDefinition :: builder ( )
125+ . measure_type ( "count" . to_string ( ) )
126+ . sql ( "COUNT(*)" . to_string ( ) )
127+ . build ( ) ,
128+ )
129+ . add_measure (
130+ "total_revenue" ,
131+ MockMeasureDefinition :: builder ( )
132+ . measure_type ( "sum" . to_string ( ) )
133+ . sql ( "revenue" . to_string ( ) )
134+ . build ( ) ,
135+ )
136+ . add_segment (
137+ "google" ,
138+ MockSegmentDefinition :: builder ( )
139+ . sql ( "{CUBE.source} = 'google'" . to_string ( ) )
140+ . build ( ) ,
141+ )
142+ . finish_cube ( )
143+ . build ( )
144+ }
145+
146+ #[ cfg( test) ]
147+ mod tests {
148+ use super :: * ;
149+ use crate :: cube_bridge:: dimension_definition:: DimensionDefinition ;
150+ use crate :: cube_bridge:: measure_definition:: MeasureDefinition ;
151+ use crate :: cube_bridge:: segment_definition:: SegmentDefinition ;
152+
153+ #[ test]
154+ fn test_schema_has_both_cubes ( ) {
155+ let schema = create_visitors_schema ( ) ;
156+
157+ assert ! ( schema. get_cube( "visitors" ) . is_some( ) ) ;
158+ assert ! ( schema. get_cube( "visitor_checkins" ) . is_some( ) ) ;
159+ }
160+
161+ #[ test]
162+ fn test_visitors_dimensions ( ) {
163+ let schema = create_visitors_schema ( ) ;
164+
165+ // Basic dimensions
166+ assert ! ( schema. get_dimension( "visitors" , "visitor_id" ) . is_some( ) ) ;
167+ assert ! ( schema. get_dimension( "visitors" , "source" ) . is_some( ) ) ;
168+ assert ! ( schema. get_dimension( "visitors" , "created_at" ) . is_some( ) ) ;
169+
170+ // Sub-query dimensions
171+ let min_checkin = schema
172+ . get_dimension ( "visitors" , "minVisitorCheckinDate" )
173+ . unwrap ( ) ;
174+ assert_eq ! ( min_checkin. static_data( ) . dimension_type, "time" ) ;
175+ assert_eq ! ( min_checkin. static_data( ) . sub_query, Some ( true ) ) ;
176+
177+ let min_checkin1 = schema
178+ . get_dimension ( "visitors" , "minVisitorCheckinDate1" )
179+ . unwrap ( ) ;
180+ assert_eq ! ( min_checkin1. static_data( ) . dimension_type, "time" ) ;
181+ assert_eq ! ( min_checkin1. static_data( ) . sub_query, Some ( true ) ) ;
182+
183+ // Geo dimension
184+ let location = schema. get_dimension ( "visitors" , "location" ) . unwrap ( ) ;
185+ assert_eq ! ( location. static_data( ) . dimension_type, "geo" ) ;
186+ assert ! ( location. has_latitude( ) . unwrap( ) ) ;
187+ assert ! ( location. has_longitude( ) . unwrap( ) ) ;
188+
189+ // Dimension with special characters
190+ let question_mark = schema. get_dimension ( "visitors" , "questionMark" ) . unwrap ( ) ;
191+ assert_eq ! ( question_mark. static_data( ) . dimension_type, "string" ) ;
192+ let sql = question_mark. sql ( ) . unwrap ( ) . unwrap ( ) ;
193+ // Verify SQL contains question marks
194+ use crate :: cube_bridge:: member_sql:: MemberSql ;
195+ use crate :: test_fixtures:: cube_bridge:: { MockSecurityContext , MockSqlUtils } ;
196+ use std:: rc:: Rc ;
197+ let ( template, _args) = sql
198+ . compile_template_sql ( Rc :: new ( MockSqlUtils ) , Rc :: new ( MockSecurityContext ) )
199+ . unwrap ( ) ;
200+ match template {
201+ crate :: cube_bridge:: member_sql:: SqlTemplate :: String ( s) => {
202+ assert ! ( s. contains( "?" ) ) ;
203+ }
204+ _ => panic ! ( "Expected String template" ) ,
205+ }
206+ }
207+
208+ #[ test]
209+ fn test_visitor_checkins_dimensions ( ) {
210+ let schema = create_visitors_schema ( ) ;
211+
212+ assert ! ( schema
213+ . get_dimension( "visitor_checkins" , "visitor_id" )
214+ . is_some( ) ) ;
215+
216+ let min_date = schema
217+ . get_dimension ( "visitor_checkins" , "minDate" )
218+ . unwrap ( ) ;
219+ assert_eq ! ( min_date. static_data( ) . dimension_type, "time" ) ;
220+
221+ let min_date1 = schema
222+ . get_dimension ( "visitor_checkins" , "minDate1" )
223+ . unwrap ( ) ;
224+ assert_eq ! ( min_date1. static_data( ) . dimension_type, "time" ) ;
225+ }
226+
227+ #[ test]
228+ fn test_visitors_measures ( ) {
229+ let schema = create_visitors_schema ( ) ;
230+
231+ let count = schema. get_measure ( "visitors" , "count" ) . unwrap ( ) ;
232+ assert_eq ! ( count. static_data( ) . measure_type, "count" ) ;
233+
234+ let revenue = schema. get_measure ( "visitors" , "total_revenue" ) . unwrap ( ) ;
235+ assert_eq ! ( revenue. static_data( ) . measure_type, "sum" ) ;
236+ }
237+
238+ #[ test]
239+ fn test_visitors_segments ( ) {
240+ let schema = create_visitors_schema ( ) ;
241+
242+ let google_segment = schema. get_segment ( "visitors" , "google" ) . unwrap ( ) ;
243+ let sql = google_segment. sql ( ) . unwrap ( ) ;
244+
245+ use crate :: cube_bridge:: member_sql:: MemberSql ;
246+ assert_eq ! ( sql. args_names( ) , & vec![ "CUBE" ] ) ;
247+ }
248+
249+ #[ test]
250+ fn test_subquery_dimension_references ( ) {
251+ let schema = create_visitors_schema ( ) ;
252+
253+ let min_checkin = schema
254+ . get_dimension ( "visitors" , "minVisitorCheckinDate" )
255+ . unwrap ( ) ;
256+ let sql = min_checkin. sql ( ) . unwrap ( ) . unwrap ( ) ;
257+
258+ use crate :: cube_bridge:: member_sql:: MemberSql ;
259+ // Should reference visitor_checkins.minDate
260+ assert_eq ! ( sql. args_names( ) , & vec![ "visitor_checkins" ] ) ;
261+ }
262+
263+ #[ test]
264+ fn test_geo_dimension_structure ( ) {
265+ use crate :: cube_bridge:: geo_item:: GeoItem ;
266+ use crate :: cube_bridge:: member_sql:: MemberSql ;
267+
268+ let schema = create_visitors_schema ( ) ;
269+
270+ let location = schema. get_dimension ( "visitors" , "location" ) . unwrap ( ) ;
271+
272+ assert_eq ! ( location. static_data( ) . dimension_type, "geo" ) ;
273+
274+ // Test using trait methods
275+ let latitude = location. latitude ( ) . unwrap ( ) . unwrap ( ) ;
276+ let lat_sql = latitude. sql ( ) . unwrap ( ) ;
277+ // Verify the SQL is correct - it should have no template parameters
278+ assert_eq ! ( lat_sql. args_names( ) . len( ) , 0 ) ;
279+
280+ let longitude = location. longitude ( ) . unwrap ( ) . unwrap ( ) ;
281+ let lon_sql = longitude. sql ( ) . unwrap ( ) ;
282+ assert_eq ! ( lon_sql. args_names( ) . len( ) , 0 ) ;
283+ }
284+ }
0 commit comments