1. Project Clover database Wed Nov 12 2025 05:07:35 UTC
  2. Package guru.mikelue.foxglove.examples

File TableFacetTest.java

 

Code metrics

2
84
23
3
599
388
26
0.31
3.65
7.67
1.13

Classes

Class Line # Actions
TableFacetTest 22 33 0% 13 2
0.9555555695.6%
TableFacetTest.NumberOfRowsTest 241 21 0% 6 0
1.0100%
TableFacetTest.ColumnValueTest 401 30 0% 7 0
1.0100%
 

Contributing tests

This file is covered by 19 tests. .

Source view

1    package guru.mikelue.foxglove.examples;
2   
3    import java.sql.JDBCType;
4    import java.util.function.Supplier;
5   
6    import org.junit.jupiter.api.AfterEach;
7    import org.junit.jupiter.api.BeforeEach;
8    import org.junit.jupiter.api.Nested;
9    import org.junit.jupiter.api.Test;
10   
11    import guru.mikelue.foxglove.ColumnMeta.Property;
12    import guru.mikelue.foxglove.functional.Int4SequenceSupplier;
13    import guru.mikelue.foxglove.functional.Suppliers;
14    import guru.mikelue.foxglove.jdbc.JdbcDataGenerator;
15    import guru.mikelue.foxglove.jdbc.JdbcTableFacet;
16    import guru.mikelue.foxglove.setting.DataSetting;
17    import guru.mikelue.foxglove.test.AbstractJdbcTestBase;
18   
19    import static guru.mikelue.foxglove.test.SampleSchema.*;
20    import static org.assertj.core.api.Assertions.assertThat;
21   
 
22    public class TableFacetTest extends AbstractJdbcTestBase {
23    private final static int RANDOM_SIZE = gen().ints().range(5, 15).get();
24   
 
25  19 toggle public TableFacetTest() {}
26   
 
27  19 toggle @BeforeEach
28    void setup() {}
29   
 
30  19 toggle @AfterEach
31    void tearDown()
32    {
33  19 deleteAll(TABLE_CAR_FEATURE, TABLE_CAR, TABLE_CAR_ARCHIVED);
34    }
35   
36    /**
37    * Example of basic usage of Foxglove.
38    */
 
39  1 toggle @Test
40    void basicUsage()
41    {
42    // tag::basicUsage[]
43    // Generates 4 rows with "cr_brand "fixed to "Toyota" and
44    // 4 different values on "cr_model"
45  1 var facet = JdbcTableFacet.builder(TABLE_CAR)
46    .numberOfRows(4)
47    .column("cr_brand")
48    .fixed("Toyota")
49    .column("cr_model")
50    .roundRobin("Corolla", "Camry", "RAV4", "Prius")
51    .build();
52   
53  1 new JdbcDataGenerator(getDataSource())
54    .generate(facet);
55    // end::basicUsage[]
56   
57  1 assertNumberOfRows(TABLE_CAR, "cr_brand = 'Toyota'")
58    .isEqualTo(4);
59    }
60   
61    /**
62    * Example of versatile configuration of a table.
63    */
 
64  1 toggle @Test
65    void versatileConfiguration()
66    {
67    // tag::versatileConfiguration[]
68  1 var randomNumber = gen().ints().range(1000, 9999);
69   
70  1 var setting = new DataSetting()
71    // Generates past date-time or JDBCType of "TIMESTAMP_WITH_TIMEZONE
72    .givenType(JDBCType.TIMESTAMP_WITH_TIMEZONE)
73    .useSupplier(
74    gen().temporal().zonedDateTime()
75    .past()
76    )
77    // Customizes text pattern for any column with "model" in its name
78    .columnMatcher(
79    columnMeta -> columnMeta.name().contains("model")
80    )
81    .useSupplier(gen().text().pattern("Model-#C#C#d#d"));
82   
83  1 var facet = JdbcTableFacet.builder(TABLE_CAR)
84    // Uses the setting on this table
85    .withSetting(setting)
86    .numberOfRows(RANDOM_SIZE)
87    // Round robin among several brands
88    .column("cr_brand")
89    .roundRobin("Toyota", "Honda", "Ford", "BMW", "Audi")
90    // Generates year between 2015 and 2025
91    .column("cr_year")
92    .useSpec(() -> gen().shorts().range((short)2015, (short)2025))
93    // Random choice for number of seats
94    .column("cr_seats")
95    .useSpec(() -> gen().oneOf(2, 4, 5, 7))
96    // Color with nullable property get nullable values
97    .<String>column("cr_color")
98    .decideSupplier(columnMeta -> {
99  1 var colorGenerator = gen().oneOf("Red", "Blue", "Green");
100   
101  1 if (columnMeta.properties().contains(Property.NULLABLE)) {
102  1 return colorGenerator.nullable();
103    }
104   
105  0 return colorGenerator;
106    })
107    // Fixed value for status column
108    .column("cr_status")
109    .fixed(null)
110    // Includes these two columns by auto-generating their values
111    .includeColumns("cr_license_plate", "cr_daily_rate", "cr_created_at", "cr_model")
112    // Alter the values for "cr_license_plate" and "cr_daily_rate"
113    .onTupleGenerated(tuple -> {
114  14 tuple.setValue(
115    "cr_license_plate",
116    tuple.getValue("cr_brand") + "-" + randomNumber.get()
117    );
118   
119  14 var dailyRate = switch (tuple.<String>getValue("cr_brand")) {
120  6 case "Toyota", "Honda" -> 0.01;
121  3 case "Ford" -> 0.02;
122  5 default -> 0.03;
123    };
124  14 tuple.setValue("cr_daily_rate", dailyRate);
125    })
126    .build();
127    // end::versatileConfiguration[]
128   
129  1 new JdbcDataGenerator(getDataSource())
130    .generate(facet);
131    }
132   
133    /**
134    * Example of modify column values after data get generated.
135    */
 
136  1 toggle @Test
137    void onTupleGenerated()
138    {
139  1 var randomNumber = gen().ints().range(1000, 9999);
140   
141    // tag::onTupleGenerated[]
142  1 var facet = JdbcTableFacet.builder(TABLE_CAR)
143    .numberOfRows(RANDOM_SIZE)
144    .column("cr_brand")
145    .roundRobin("Toyota", "Honda", "Ford", "BMW", "Audi")
146    // Sets the value of "cr_license_plate" by combination of "cr_brand" and a random number
147    .onTupleGenerated(tuple -> tuple.setValue(
148    "cr_license_plate",
149    tuple.getValue("cr_brand") + "-" + randomNumber.get()
150    ))
151    .build();
152    // end::onTupleGenerated[]
153   
154  1 new JdbcDataGenerator(getDataSource())
155    .generate(facet);
156   
157  1 assertNumberOfRows(
158    TABLE_CAR, " cr_license_plate LIKE CONCAT(cr_brand, '%')"
159    )
160    .isEqualTo(RANDOM_SIZE);
161    }
162   
163    /**
164    * Example for setting on {@link JdbcTableFacet}.
165    */
 
166  1 toggle @Test
167    void setting()
168    {
169  1 final String sampleText = gen().text().word().get();
170  1 var suffixSupplier = gen().text().pattern("#C#C#d#d");
171   
172    // tag::settingOnTable[]
173  1 var setting = new DataSetting()
174    // Excludes any column with JDBCType of OTHER
175    .excludeWhen(columnMeta -> columnMeta.jdbcType() == JDBCType.OTHER)
176    // For any column with type of "VARCHAR", using the supplier
177    .givenType(JDBCType.VARCHAR)
178    .useSupplier(() -> sampleText + suffixSupplier.get())
179    // For not-nullable SMALLINT columns, generating values between 2010 and 2020
180    .columnMatcher(
181    columnMeta -> columnMeta.jdbcType() == JDBCType.SMALLINT &&
182    !columnMeta.properties().contains(Property.NULLABLE)
183    )
184    .useSupplier(gen().shorts().range((short)2010, (short)2020)::get);
185   
186  1 var facet = JdbcTableFacet.builder(TABLE_CAR)
187    // Uses the setting on the whole table
188    .withSetting(setting)
189    .numberOfRows(RANDOM_SIZE)
190    .build();
191    // end::settingOnTable[]
192   
193  1 new JdbcDataGenerator(getDataSource())
194    .generate(facet);
195   
196  1 assertNumberOfRows(
197    TABLE_CAR,
198    " cr_brand LIKE '" + sampleText + "%' AND" +
199    " cr_year BETWEEN 2010 AND 2020"
200    )
201    .isEqualTo(RANDOM_SIZE);
202    }
203   
204    /**
205    * Example for inclusion of a table.
206    */
 
207  1 toggle @Test
208    void inclusion()
209    {
210    // tag::includeColumns[]
211  1 var tableFacet = JdbcTableFacet.builder(TABLE_CAR)
212    .numberOfRows(RANDOM_SIZE)
213    // Must put all of the columns which are not-nullable, no-default value.
214    .includeColumns("cr_license_plate", "cr_brand", "cr_model", "cr_year")
215    .build();
216    // end::includeColumns[]
217   
218  1 new JdbcDataGenerator(getDataSource())
219    .generate(tableFacet);
220    }
221   
222    /**
223    * Example for exclusion of a table.
224    */
 
225  1 toggle @Test
226    void exclusion()
227    {
228    // tag::excludeColumns[]
229  1 var tableFacet = JdbcTableFacet.builder(TABLE_CAR)
230    .numberOfRows(RANDOM_SIZE)
231    // These columns would not be generated by Foxglove
232    .excludeColumns("cr_seats", "cr_color", "cr_status", "cr_created_at", "cr_updated_at")
233    .build();
234    // end::excludeColumns[]
235   
236  1 new JdbcDataGenerator(getDataSource())
237    .generate(tableFacet);
238    }
239   
240    @Nested
 
241    class NumberOfRowsTest {
242    /**
243    * Example for fixed number of rows to be generated.
244    */
 
245  1 toggle @Test
246    void numberOfRows()
247    {
248    // tag::numberOfRows[]
249  1 var facet = JdbcTableFacet.builder(TABLE_CAR)
250    .numberOfRows(RANDOM_SIZE)
251    .build();
252    // end::numberOfRows[]
253   
254  1 var generatedNumber = dataGenerator()
255    .generate(facet);
256   
257  1 assertThat(generatedNumber)
258    .isEqualTo(RANDOM_SIZE);
259    }
260   
261    /**
262    * Example for bound range of a key column.
263    */
 
264  1 toggle @Test
265    void keyColumn()
266    {
267    // tag::keyColumn[]
268    // The range of cr_id is between 1000 and 1000 + RANDOM_SIZE - 1
269  1 var facet = JdbcTableFacet.builder(TABLE_CAR)
270    .keyOfInt("cr_id").limit(1000, RANDOM_SIZE)
271    .build();
272    // end::keyColumn[]
273   
274  1 dataGenerator().generate(facet);
275   
276  1 assertNumberOfRows(
277    TABLE_CAR,
278    String.format("cr_id BETWEEN %d AND %d",
279    1000,
280    1000 + RANDOM_SIZE - 1
281    )
282    )
283    .isEqualTo(RANDOM_SIZE);
284    }
285   
286    /**
287    * Example for Cartesian product of multiple columns.
288    */
 
289  1 toggle @Test
290    void cartesianProduct()
291    {
292    // tag::cartesianProduct[]
293    // Generates 3 (brands) * 3 (years) = 9 rows
294  1 var facet = JdbcTableFacet.builder(TABLE_CAR)
295    .cartesianProduct("cr_brand")
296    .domain("Toyota", "Honda", "Ford")
297    .cartesianProduct("cr_year")
298    .domain(2020, 2021, 2022)
299    .build();
300    // end::cartesianProduct[]
301   
302  1 dataGenerator().generate(facet);
303   
304  1 assertNumberOfRows(
305    TABLE_CAR,
306    " cr_brand IN ('Toyota', 'Honda', 'Ford') AND" +
307    " cr_year IN (2020, 2021, 2022)"
308    )
309    .isEqualTo(9);
310    }
311   
312    /**
313    * Example for referencing column of another table.
314    */
 
315  1 toggle @Test
316    void referencing()
317    {
318    // tag::referencing[]
319    // Prepares parent facet
320  1 var carFacet = JdbcTableFacet.builder(TABLE_CAR)
321    .numberOfRows(RANDOM_SIZE)
322    .build();
323   
324    // Generates rows referencing to the parent facet.
325    // The number of features is equal to "RANDOM_SIZE".
326  1 var featureFacet = JdbcTableFacet.builder(TABLE_CAR_FEATURE)
327    .referencing("cf_cr_id").parent(carFacet, "cr_id")
328    // Every car is referenced by one feature.
329    .cardinality(1)
330    .column("cf_feature_name")
331    .fixed("Sunroof")
332    .build();
333    // end::referencing[]
334   
335  1 dataGenerator()
336    .generate(carFacet, featureFacet);
337   
338  1 assertNumberOfRows(
339    TABLE_CAR_FEATURE,
340    "cf_feature_name = 'Sunroof'"
341    )
342    .isEqualTo(RANDOM_SIZE);
343    }
344   
345    /**
346    * Example for fixed domain.
347    */
 
348  1 toggle @Test
349    void fixedDomain()
350    {
351    // tag::fixedDomain[]
352    // Prepares list of colors
353  1 var colors = gen().oneOf("Red", "Blue", "Green")
354    .list(RANDOM_SIZE);
355   
356  1 var facet = JdbcTableFacet.builder(TABLE_CAR)
357    // The number of rows is equal to the size of colors list
358    .numberOfRows(colors.size())
359    // Use round robin for these colors
360    .column("cr_color").roundRobin(colors)
361    .build();
362    // end::fixedDomain[]
363   
364  1 dataGenerator().generate(facet);
365   
366  1 assertNumberOfRows(
367    TABLE_CAR,
368    "cr_color IN ('Red', 'Blue', 'Green')"
369    )
370    .isEqualTo(colors.size());
371    }
372   
373    /**
374    * Example for fixed domain.
375    */
 
376  1 toggle @Test
377    void cartesianProductOnOneColumn()
378    {
379    // tag::cartesianProductOnOneColumn[]
380    // Prepares list of colors
381  1 var colors = gen().oneOf("Red", "Blue", "Green")
382    .list(RANDOM_SIZE);
383   
384  1 var facet = JdbcTableFacet.builder(TABLE_CAR)
385    // Use round robin for these colors
386    .cartesianProduct("cr_color").domain(colors)
387    .build();
388    // end::cartesianProductOnOneColumn[]
389   
390  1 dataGenerator().generate(facet);
391   
392  1 assertNumberOfRows(
393    TABLE_CAR,
394    "cr_color IN ('Red', 'Blue', 'Green')"
395    )
396    .isEqualTo(colors.size());
397    }
398    }
399   
400    @Nested
 
401    class ColumnValueTest {
402    /**
403    * Example for fixed value of a column.
404    */
 
405  1 toggle @Test
406    void fixed()
407    {
408  1 var sampleColor = "red";
409   
410    // tag::fixed[]
411  1 var facet = JdbcTableFacet.builder(TABLE_CAR)
412    .numberOfRows(RANDOM_SIZE)
413    .column("cr_color").fixed(sampleColor)
414    .build();
415    // end::fixed[]
416   
417  1 dataGenerator().generate(facet);
418   
419  1 assertNumberOfRows(
420    TABLE_CAR,
421    String.format("cr_color = '%s'", sampleColor)
422    )
423    .isEqualTo(RANDOM_SIZE);
424    }
425   
426    /**
427    * Example for customizing random value of a column.
428    */
 
429  1 toggle @Test
430    void randomValue()
431    {
432    // tag::randomValue[]
433    // Generates random year between 2015 and 2025
434  1 var yearSupplier = gen().ints().range(2015, 2025);
435  1 var facet = JdbcTableFacet.builder(TABLE_CAR)
436    .numberOfRows(RANDOM_SIZE)
437    .column("cr_year").useSupplier(yearSupplier)
438    .build();
439    // end::randomValue[]
440   
441  1 dataGenerator().generate(facet);
442   
443  1 assertNumberOfRows(
444    TABLE_CAR,
445    "cr_year BETWEEN 2015 AND 2025"
446    )
447    .isEqualTo(RANDOM_SIZE);
448    }
449   
450    /**
451    * Example for value generator by round robin.
452    */
 
453  1 toggle @Test
454    void roundRobin()
455    {
456    // tag::roundRobin[]
457    // Round robin among BMW, Audi, and Mercedes
458  1 var facet = JdbcTableFacet.builder(TABLE_CAR)
459    .numberOfRows(RANDOM_SIZE)
460    .column("cr_brand")
461    .roundRobin("BMW", "Audi", "Mercedes")
462    .build();
463    // end::roundRobin[]
464   
465  1 dataGenerator().generate(facet);
466   
467  1 assertNumberOfRows(
468    TABLE_CAR,
469    "cr_brand IN ('BMW', 'Audi', 'Mercedes')"
470    )
471    .isEqualTo(RANDOM_SIZE);
472    }
473   
474    /**
475    * Example for value generator by sequence number.
476    */
 
477  1 toggle @Test
478    void sequenceNumber()
479    {
480    // tag::sequenceNumber[]
481    // Generates license plate like CAR-01, CAR-02, ...
482  1 var sequence = new Int4SequenceSupplier(2, 2);
483  1 Supplier<String> plateSupplier = () -> String.format(
484    "CAR-%02d",
485    sequence.getAsInt()
486    );
487  1 var facet = JdbcTableFacet.builder(TABLE_CAR)
488    .numberOfRows(RANDOM_SIZE)
489    .column("cr_license_plate").useSupplier(plateSupplier)
490    .build();
491    // end::sequenceNumber[]
492   
493  1 dataGenerator().generate(facet);
494   
495  1 assertNumberOfRows(
496    TABLE_CAR,
497    String.format(
498    "cr_license_plate LIKE 'CAR-%%'",
499    RANDOM_SIZE
500    )
501    )
502    .isEqualTo(RANDOM_SIZE);
503    }
504   
505    /**
506    * Example for nullable value by Instancio generator.
507    */
 
508  1 toggle @Test
509    void nullByInstancio()
510    {
511  1 final int ROWS = gen().ints().range(60, 300).get();
512   
513    // tag::nullByInstancio[]
514    // Generates colors(red, blue, green, or NULL) and 1/6 odds to be null
515  1 var colorSupplier = gen().oneOf("red", "blue", "green")
516    .nullable();
517  1 var facet = JdbcTableFacet.builder(TABLE_CAR)
518    .numberOfRows(ROWS)
519    .column("cr_color").useSupplier(colorSupplier)
520    .build();
521    // end::nullByInstancio[]
522   
523  1 dataGenerator().generate(facet);
524   
525  1 assertNumberOfRows(
526    TABLE_CAR,
527    "cr_color IS NULL"
528    )
529    .isGreaterThan(0);
530    }
531   
532    /**
533    * Example for nullable value by customizing odds.
534    */
 
535  1 toggle @Test
536    void nullByCustomizedOdds()
537    {
538  1 final int ROWS = gen().ints().range(20, 100).get();
539   
540    // tag::nullByCustomizedOdds[]
541    // Generates colors(red, blue, green, or NULL) and 50% chance to be null
542  1 var colorSupplier = Suppliers.rollingSupplier(
543    gen().oneOf("red", "blue", "green"), 2
544    );
545  1 var facet = JdbcTableFacet.builder(TABLE_CAR)
546    .numberOfRows(ROWS)
547    .column("cr_color").useSupplier(colorSupplier)
548    .build();
549    // end::nullByCustomizedOdds[]
550   
551  1 dataGenerator().generate(facet);
552   
553  1 assertNumberOfRows(
554    TABLE_CAR,
555    "cr_color IS NULL"
556    )
557    .isGreaterThan(0);
558    }
559   
560    /**
561    * Example for value domain from another table.
562    */
 
563  1 toggle @Test
564    void from()
565    {
566    // tag::from[]
567    // Prepares parent facet
568  1 var carFacet = JdbcTableFacet.builder(TABLE_CAR)
569    .numberOfRows(RANDOM_SIZE)
570    .column("cr_color")
571    .roundRobin("Red", "Blue", "Green")
572    .build();
573   
574  1 var carArchivedFacet = JdbcTableFacet.builder(TABLE_CAR_ARCHIVED)
575    .numberOfRows(10)
576    // Has no effect on the number of rows
577    .column("ca_color")
578    .from(carFacet, "cr_color")
579    // Chooses the values of cars' colors randomly
580    .random()
581    .build();
582    // end::from[]
583   
584  1 dataGenerator()
585    .generate(carFacet, carArchivedFacet);
586   
587  1 assertNumberOfRows(
588    TABLE_CAR_ARCHIVED,
589    "ca_color IN ('Red', 'Blue', 'Green')"
590    )
591    .isEqualTo(10);
592    }
593    }
594   
 
595  13 toggle private JdbcDataGenerator dataGenerator()
596    {
597  13 return new JdbcDataGenerator(getDataSource());
598    }
599    }