github.com/square/finch@v0.0.0-20240412205204-6530c03e2b96/boot/scope_test.go (about)

     1  package boot_test
     2  
     3  import (
     4  	"context"
     5  	"database/sql"
     6  	"fmt"
     7  	"os"
     8  	"testing"
     9  
    10  	"github.com/go-test/deep"
    11  
    12  	"github.com/square/finch/boot"
    13  	"github.com/square/finch/test"
    14  )
    15  
    16  var (
    17  	dsn string
    18  	db  *sql.DB
    19  )
    20  
    21  func setup(t *testing.T) {
    22  	var err error
    23  	var queries []string
    24  	if dsn == "" || db == nil {
    25  		dsn, db, err = test.Connection()
    26  		if err != nil {
    27  			t.Fatal(err)
    28  		}
    29  		queries = []string{
    30  			"CREATE DATABASE IF NOT EXISTS finch",
    31  			"USE finch",
    32  			"DROP TABLE IF EXISTS scopetest",
    33  			"CREATE TABLE scopetest (id int auto_increment primary key not null, eg int, cg int, c int, iter int, trx int, s int, a int, r int)",
    34  		}
    35  	} else {
    36  		queries = []string{
    37  			"USE finch",
    38  			"TRUNCATE TABLE scopetest",
    39  		}
    40  	}
    41  	if err := test.Exec(db, queries); err != nil {
    42  		t.Fatal(err)
    43  	}
    44  }
    45  
    46  func results() ([][]int, error) {
    47  	rows, err := db.QueryContext(context.Background(), "select eg, cg, c, iter, trx, s, a, r from finch.scopetest order by eg, cg, c, iter, trx, s")
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  	defer rows.Close()
    52  	res := [][]int{}
    53  	for rows.Next() {
    54  		cols := make([]int, 8)
    55  		ptr := make([]interface{}, len(cols))
    56  		for i := range ptr {
    57  			ptr[i] = &cols[i]
    58  		}
    59  		if err = rows.Scan(ptr...); err != nil {
    60  			return nil, err
    61  		}
    62  		res = append(res, cols)
    63  	}
    64  	return res, nil
    65  }
    66  
    67  const (
    68  	EG int = iota
    69  	CG
    70  	C
    71  	ITER
    72  	TRX
    73  	S
    74  	A
    75  	R
    76  )
    77  
    78  func colVals(col int, rows [][]int) []int {
    79  	vals := make([]int, len(rows))
    80  	for i := range rows {
    81  		vals[i] = rows[i][col]
    82  	}
    83  	return vals
    84  }
    85  
    86  func hasPattern(p string, vals []int) (bool, string) {
    87  	az := 96 // 97=a
    88  	seen := map[int]bool{}
    89  	q := ""
    90  	for _, v := range vals {
    91  		if !seen[v] {
    92  			az += 1 // a, c, ...
    93  			seen[v] = true
    94  		}
    95  		q += fmt.Sprintf("%c", az)
    96  	}
    97  	return p == q, q
    98  }
    99  
   100  func run(t *testing.T, file string) ([][]int, error) {
   101  	setup(t)
   102  	defer os.Chdir(cwd) // Finch will cd to stage file dir
   103  	env := boot.Env{
   104  		Args: []string{
   105  			"./finch", // fake like it was run from cmd line (required)
   106  			"-D", "finch",
   107  			"--dsn", dsn,
   108  			"../test/run/scope/" + file,
   109  		},
   110  	}
   111  	err := boot.Up(env)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	rows, err := results()
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  	if len(rows) != 12 {
   120  		return nil, fmt.Errorf("got %d rows, expected 12", len(rows))
   121  	}
   122  	return rows, nil
   123  }
   124  
   125  // --------------------------------------------------------------------------
   126  
   127  func TestScope_Statement(t *testing.T) {
   128  	if test.Build {
   129  		t.Skip("GitHub Actions build")
   130  	}
   131  	rows, err := run(t, "statement.yaml")
   132  	if err != nil {
   133  		t.Fatal(err)
   134  	}
   135  
   136  	/*
   137  		+------+------+------+------+------+------+------+--------+
   138  		| eg   | cg   | c    | iter | trx  | s    | a    | r      |
   139  		+------+------+------+------+------+------+------+--------+
   140  		|    1 |    1 |    1 |    1 |    1 |    1 |    1 | 550747 |
   141  		|    1 |    1 |    1 |    1 |    1 |    2 |    1 | 736643 |
   142  		|    1 |    1 |    1 |    1 |    2 |    3 |    1 | 421079 |
   143  		|    1 |    1 |    1 |    2 |    3 |    4 |    2 | 662756 |
   144  		|    1 |    1 |    1 |    2 |    3 |    5 |    2 | 135600 |
   145  		|    1 |    1 |    1 |    2 |    4 |    6 |    2 | 331290 |
   146  		|    1 |    2 |    1 |    1 |    1 |    1 |    1 | 952510 |
   147  		|    1 |    2 |    1 |    1 |    1 |    2 |    1 | 163171 |
   148  		|    1 |    2 |    1 |    1 |    2 |    3 |    1 | 979482 |
   149  		|    2 |    1 |    1 |    1 |    1 |    1 |    1 | 351387 |
   150  		|    2 |    1 |    1 |    1 |    1 |    2 |    1 | 702906 |
   151  		|    2 |    1 |    1 |    1 |    2 |    3 |    1 | 198349 |
   152  		+------+------+------+------+------+------+------+--------+
   153  	*/
   154  
   155  	// A (auto-inc) values equal the above ^ example
   156  	got := colVals(A, rows)
   157  	expect := []int{1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1}
   158  	if diff := deep.Equal(got, expect); diff != nil {
   159  		t.Errorf("wrong auto-inc values: %v", diff)
   160  	}
   161  
   162  	got = colVals(R, rows)
   163  	seen := map[int]bool{}
   164  	for _, i := range got {
   165  		if seen[i] {
   166  			t.Errorf("same random value with statement scope, expected all random values (or 1 in a 1,000,000 chance @r generated same random value): %d", i)
   167  		}
   168  	}
   169  }
   170  
   171  func TestScope_Trx(t *testing.T) {
   172  	if test.Build {
   173  		t.Skip("GitHub Actions build")
   174  	}
   175  
   176  	rows, err := run(t, "trx.yaml")
   177  	if err != nil {
   178  		t.Fatal(err)
   179  	}
   180  
   181  	/*
   182  		+------+------+------+------+------+------+------+--------+
   183  		| eg   | cg   | c    | iter | trx  | s    | a    | r      |
   184  		+------+------+------+------+------+------+------+--------+
   185  		|    1 |    1 |    1 |    1 |    1 |    1 |    1 | 566273 | a
   186  		|    1 |    1 |    1 |    1 |    1 |    2 |    1 | 566273 | a
   187  		|    1 |    1 |    1 |    1 |    2 |    3 |    1 | 995357 | b
   188  		|    1 |    1 |    1 |    2 |    3 |    4 |    2 | 919473 | c
   189  		|    1 |    1 |    1 |    2 |    3 |    5 |    2 | 919473 | c
   190  		|    1 |    1 |    1 |    2 |    4 |    6 |    2 | 509510 | d
   191  		|    1 |    2 |    1 |    1 |    1 |    1 |    1 | 151654 | e
   192  		|    1 |    2 |    1 |    1 |    1 |    2 |    1 | 151654 | e
   193  		|    1 |    2 |    1 |    1 |    2 |    3 |    1 | 418694 | f
   194  		|    2 |    1 |    1 |    1 |    1 |    1 |    1 | 495910 | g
   195  		|    2 |    1 |    1 |    1 |    1 |    2 |    1 | 495910 | g
   196  		|    2 |    1 |    1 |    1 |    2 |    3 |    1 | 841785 | h
   197  		+------+------+------+------+------+------+------+--------+
   198  	*/
   199  
   200  	// A (auto-inc) values equal the above ^ example, which is same as statement
   201  	// scope, so looking at r for trx scope is better (below)
   202  	got := colVals(A, rows)
   203  	expect := []int{1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1}
   204  	if diff := deep.Equal(got, expect); diff != nil {
   205  		t.Errorf("wrong auto-inc values: %v", diff)
   206  	}
   207  
   208  	// Check that random numbers repeat as expected by reducing each unique
   209  	// value to a, b, c, etc. then comparing the patterns
   210  	rVals := colVals(R, rows)
   211  	p := "aabccdeefggh"
   212  	if ok, q := hasPattern(p, rVals); !ok {
   213  		t.Errorf("random vals %v have pattern %s, expected %s ", rVals, q, p)
   214  	}
   215  }
   216  
   217  func TestScope_Iter(t *testing.T) {
   218  	if test.Build {
   219  		t.Skip("GitHub Actions build")
   220  	}
   221  
   222  	rows, err := run(t, "iter.yaml")
   223  	if err != nil {
   224  		t.Fatal(err)
   225  	}
   226  
   227  	/*
   228  		+------+------+------+------+------+------+------+--------+
   229  		| eg   | cg   | c    | iter | trx  | s    | a    | r      |
   230  		+------+------+------+------+------+------+------+--------+
   231  		|    1 |    1 |    1 |    1 |    1 |    1 |    1 | 304611 | a
   232  		|    1 |    1 |    1 |    1 |    1 |    2 |    1 | 304611 | a
   233  		|    1 |    1 |    1 |    1 |    2 |    3 |    1 | 304611 | a
   234  		|    1 |    1 |    1 |    2 |    3 |    4 |    2 | 945546 | b
   235  		|    1 |    1 |    1 |    2 |    3 |    5 |    2 | 945546 | b
   236  		|    1 |    1 |    1 |    2 |    4 |    6 |    2 | 945546 | b
   237  		|    1 |    2 |    1 |    1 |    1 |    1 |    1 | 777638 | c
   238  		|    1 |    2 |    1 |    1 |    1 |    2 |    1 | 777638 | c
   239  		|    1 |    2 |    1 |    1 |    2 |    3 |    1 | 777638 | c
   240  		|    2 |    1 |    1 |    1 |    1 |    1 |    1 | 553149 | d
   241  		|    2 |    1 |    1 |    1 |    1 |    2 |    1 | 553149 | d
   242  		|    2 |    1 |    1 |    1 |    2 |    3 |    1 | 553149 | d
   243  		+------+------+------+------+------+------+------+--------+
   244  	*/
   245  
   246  	// A (auto-inc) values equal the above ^ example, which is same as statement
   247  	// scope, so looking at r for trx scope is better (below)
   248  	got := colVals(A, rows)
   249  	expect := []int{1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1}
   250  	if diff := deep.Equal(got, expect); diff != nil {
   251  		t.Errorf("wrong auto-inc values: %v", diff)
   252  	}
   253  
   254  	// See previous test
   255  	rVals := colVals(R, rows)
   256  	p := "aaabbbcccddd"
   257  	if ok, q := hasPattern(p, rVals); !ok {
   258  		t.Errorf("random vals %v have pattern %s, expected %s ", rVals, q, p)
   259  	}
   260  }
   261  
   262  func TestScope_Client(t *testing.T) {
   263  	if test.Build {
   264  		t.Skip("GitHub Actions build")
   265  	}
   266  
   267  	rows, err := run(t, "client.yaml")
   268  	if err != nil {
   269  		t.Fatal(err)
   270  	}
   271  
   272  	/*
   273  		+------+------+------+------+------+------+------+--------+
   274  		| eg   | cg   | c    | iter | trx  | s    | a    | r      |
   275  		+------+------+------+------+------+------+------+--------+
   276  		|    1 |    1 |    1 |    1 |    1 |    1 |    1 | 782998 | a
   277  		|    1 |    1 |    1 |    1 |    1 |    2 |    1 | 782998 | a
   278  		|    1 |    1 |    1 |    1 |    2 |    3 |    1 | 782998 | a
   279  		|    1 |    1 |    1 |    2 |    3 |    4 |    1 | 782998 | a
   280  		|    1 |    1 |    1 |    2 |    3 |    5 |    1 | 782998 | a
   281  		|    1 |    1 |    1 |    2 |    4 |    6 |    1 | 782998 | a
   282  		|    1 |    2 |    1 |    1 |    1 |    1 |    1 |  88789 | b
   283  		|    1 |    2 |    1 |    1 |    1 |    2 |    1 |  88789 | b
   284  		|    1 |    2 |    1 |    1 |    2 |    3 |    1 |  88789 | b
   285  		|    2 |    1 |    1 |    1 |    1 |    1 |    1 |  93318 | c
   286  		|    2 |    1 |    1 |    1 |    1 |    2 |    1 |  93318 | c
   287  		|    2 |    1 |    1 |    1 |    2 |    3 |    1 |  93318 | c
   288  		+------+------+------+------+------+------+------+--------+
   289  	*/
   290  
   291  	got := colVals(A, rows)
   292  	expect := []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
   293  	if diff := deep.Equal(got, expect); diff != nil {
   294  		t.Errorf("wrong auto-inc values: %v", diff)
   295  	}
   296  
   297  	// See previous test
   298  	rVals := colVals(R, rows)
   299  	p := "aaaaaabbbccc"
   300  	if ok, q := hasPattern(p, rVals); !ok {
   301  		t.Errorf("random vals %v have pattern %s, expected %s ", rVals, q, p)
   302  	}
   303  }