@ -19,7 +19,21 @@ import (
)
)
var defaultFixedTime = time . Date ( 2026 , 1 , 25 , 10 , 10 , 40 , 0 , time . UTC )
var defaultFixedTime = time . Date ( 2026 , 1 , 25 , 10 , 10 , 40 , 0 , time . UTC )
var getAllReturnColumns = [ ] string { "id" , "created_at" , "title" , "year" , "runtime" , "genres" , "version" }
var getAllReturnColumns = [ ] string { "count" , "id" , "created_at" , "title" , "year" , "runtime" , "genres" , "version" }
// This regex matches the SQL query structure while allowing flexible whitespace and any values substituted into the ORDER BY fmt.Sprintf("%s %s") fields.
// The ORDER BY column and direction are intentionally matched with broad patterns since pgxmock only needs to validate the overall query shape.
// const getAllQueryRegex = `(?is)
// SELECT\s+count\(\*\)\s+OVER\(\),\s+id,\s+created_at,\s+title,\s+year,\s+runtime,\s+genres,\s+version\s+
// FROM\s+movies\s+
// WHERE\s+\(to_tsvector\('simple',\s+title\)\s+@@\s+plainto_tsquery\('simple',\s+\$1\)\s+OR\s+\$1\s+=\s+''\)\s+
// AND\s+\(genres\s+@>\s+\$2\s+OR\s+\$2\s+=\s+'\{\}'\)\s+
// ORDER\s+BY\s+.+\s+.+,\s+id\s+ASC\s+
// LIMIT\s+\$3\s+OFFSET\s+\$4`
// This regex matches the SQL query structure while tolerating whitespace normalization performed by pgx.
// The ORDER BY column and direction generated by fmt.Sprintf("%s %s") are matched generically so tests do not depend on exact formatting.
const getAllQueryRegex = ` (?is)^SELECT\s+count\(\*\)\s+OVER\(\),\s+id,\s+created_at,\s+title,\s+year,\s+runtime,\s+genres,\s+version\s+FROM\s+movies\s+WHERE\s+\(to_tsvector\('simple',\s+title\)\s+@@\s+plainto_tsquery\('simple',\s+\$1\)\s+OR\s+\$1\s+=\s+''\)\s+AND\s+\(genres\s+@>\s+\$2\s+OR\s+\$2\s+=\s+'\ { \}'\)\s+ORDER\s+BY\s+\S+\s+\S+,\s+id\s+ASC\s+LIMIT\s+\$3\s+OFFSET\s+\$4$ `
func TestHealthRoute ( t * testing . T ) {
func TestHealthRoute ( t * testing . T ) {
respRec := httptest . NewRecorder ( )
respRec := httptest . NewRecorder ( )
@ -220,7 +234,8 @@ func TestGetMovieHandler(t *testing.T) {
} ,
} ,
}
}
mockPool . ExpectQuery ( "SELECT id, created_at, title, year, runtime, genres, version FROM movies" ) .
//(?i) This is a flag modifier that turns on case-insensitive matching
mockPool . ExpectQuery ( ` (?i)SELECT\s+id,\s+created_at,\s+title,\s+year,\s+runtime,\s+genres,\s+version\s+FROM\s+movies\s+WHERE\s+id ` ) .
WithArgs ( int64 ( 1337 ) ) . WillReturnRows (
WithArgs ( int64 ( 1337 ) ) . WillReturnRows (
pgxmock . NewRows ( [ ] string { "id" , "created_at" , "title" , "year" , "runtime" , "genres" , "version" } ) .
pgxmock . NewRows ( [ ] string { "id" , "created_at" , "title" , "year" , "runtime" , "genres" , "version" } ) .
AddRow ( int64 ( 1337 ) , time . Now ( ) , "a laura is born" , 1990 , 36 , [ ] string { "family" , "wife" } , 1 ) , // These values will be scanned into the struct
AddRow ( int64 ( 1337 ) , time . Now ( ) , "a laura is born" , 1990 , 36 , [ ] string { "family" , "wife" } , 1 ) , // These values will be scanned into the struct
@ -321,14 +336,20 @@ func TestListMovieHandler(t *testing.T) {
} ,
} ,
}
}
rows := pgxmock . NewRows ( [ ] string { " id", "created_at" , "title" , "year" , "runtime" , "genres" , "version" } )
rows := pgxmock . NewRows ( [ ] string { " count", " id", "created_at" , "title" , "year" , "runtime" , "genres" , "version" } )
for _ , m := range movies {
for _ , m := range movies {
rows . AddRow ( m . ID , m . CreatedAt , m . Title , m . Year , m . Runtime , m . Genres , m . Version )
rows . AddRow ( 2 ,
m . ID , m . CreatedAt , m . Title , m . Year , m . Runtime , m . Genres , m . Version )
}
}
mockPool . ExpectQuery ( ` SELECT id , created_at , title , year , runtime , genres , version
mockPool . ExpectQuery ( getAllQueryRegex ) .
FROM movies
WithArgs (
ORDER BY id ASC ` ) . WillReturnRows ( rows )
pgxmock . AnyArg ( ) ,
pgxmock . AnyArg ( ) ,
pgxmock . AnyArg ( ) ,
pgxmock . AnyArg ( ) ,
) .
WillReturnRows ( rows )
r , err := http . NewRequest ( http . MethodGet , "/v1/movies" , nil )
r , err := http . NewRequest ( http . MethodGet , "/v1/movies" , nil )
assert . NilError ( t , err )
assert . NilError ( t , err )
@ -363,9 +384,16 @@ func TestListHandlerServerError(t *testing.T) {
respRec := httptest . NewRecorder ( )
respRec := httptest . NewRecorder ( )
mockPool , err := pgxmock . NewPool ( )
mockPool , err := pgxmock . NewPool ( )
assert . NilError ( t , err )
assert . NilError ( t , err )
errorRows := pgxmock . NewRows ( getAllReturnColumns ) . AddRow ( 1 , time . Now ( ) , "will error" , 2026 , 120 , [ ] string { } , 1 ) . RowError ( 0 , fmt . Errorf ( "network connection lost" ) )
errorRows := pgxmock . NewRows ( getAllReturnColumns ) . AddRow ( 1 , 1 , time . Now ( ) , "will error" , 2026 , 120 , [ ] string { } , 1 ) . RowError ( 0 , fmt . Errorf ( "network connection lost" ) )
mockPool . ExpectQuery ( "SELECT" ) . WillReturnRows ( errorRows )
mockPool . ExpectQuery ( "SELECT" ) .
WithArgs (
pgxmock . AnyArg ( ) ,
pgxmock . AnyArg ( ) ,
pgxmock . AnyArg ( ) ,
pgxmock . AnyArg ( ) ,
) .
WillReturnRows ( errorRows )
r , err := http . NewRequest ( http . MethodGet , "/v1/movies" , nil )
r , err := http . NewRequest ( http . MethodGet , "/v1/movies" , nil )
app := newTestApplication ( mockPool )
app := newTestApplication ( mockPool )
@ -381,11 +409,11 @@ func TestListHandlerValidation(t *testing.T) {
assert . NilError ( t , err )
assert . NilError ( t , err )
defer mockPool . Close ( )
defer mockPool . Close ( )
getAllQuery := `
// getAllQuery := `
SELECT id , created_at , title , year , runtime , genres , version
// SELECT count, id, created_at, title, year, runtime, genres, version
FROM movies
// FROM movies
ORDER BY id ASC
// ORDER BY id ASC
`
// `
testTable := [ ] struct {
testTable := [ ] struct {
name string
name string
@ -487,7 +515,14 @@ func TestListHandlerValidation(t *testing.T) {
if test . wantCode == http . StatusOK {
if test . wantCode == http . StatusOK {
// TODO expand return values and parameterize
// TODO expand return values and parameterize
// empty return is fine for validator test
// empty return is fine for validator test
mockPool . ExpectQuery ( getAllQuery ) . WillReturnRows ( mockPool . NewRows ( getAllReturnColumns ) )
mockPool . ExpectQuery ( getAllQueryRegex ) .
WithArgs (
pgxmock . AnyArg ( ) ,
pgxmock . AnyArg ( ) ,
pgxmock . AnyArg ( ) ,
pgxmock . AnyArg ( ) ,
) .
WillReturnRows ( mockPool . NewRows ( getAllReturnColumns ) )
}
}
r , err := http . NewRequest ( http . MethodGet , test . query , nil )
r , err := http . NewRequest ( http . MethodGet , test . query , nil )
@ -587,10 +622,16 @@ func TestListMoviesFilters(t *testing.T) {
rows := pgxmock . NewRows ( getAllReturnColumns )
rows := pgxmock . NewRows ( getAllReturnColumns )
for _ , m := range test . expextedMovies {
for _ , m := range test . expextedMovies {
rows . AddRow ( m . ID , m . CreatedAt , m . Title , m . Year , m . Runtime , m . Genres , m . Version )
rows . AddRow ( 3 , m . ID , m . CreatedAt , m . Title , m . Year , m . Runtime , m . Genres , m . Version )
}
}
mockPool . ExpectQuery ( "SELECT" ) .
mockPool . ExpectQuery ( "SELECT" ) .
WithArgs (
pgxmock . AnyArg ( ) ,
pgxmock . AnyArg ( ) ,
pgxmock . AnyArg ( ) ,
pgxmock . AnyArg ( ) ,
) .
WillReturnRows ( rows )
WillReturnRows ( rows )
assert . NilError ( t , err )
assert . NilError ( t , err )