diff --git a/api/core/administrator/id.go b/api/core/administrator/id.go index dcee051..35d9e54 100644 --- a/api/core/administrator/id.go +++ b/api/core/administrator/id.go @@ -17,6 +17,12 @@ func GetUserID(db *pgxpool.Pool, ctx context.Context, key string) (int, error) { } defer c.Release() + tx, err := c.Begin(ctx) + if err != nil { + return 0, err + } + defer tx.Rollback(ctx) + sql, args, err := query. Update("administrators"). Set("last_used", time.Now().UTC().Format(time.RFC3339)). @@ -25,11 +31,10 @@ func GetUserID(db *pgxpool.Pool, ctx context.Context, key string) (int, error) { return 0, err } - r, err := c.Query(ctx, sql, args...) + _, err = tx.Exec(ctx, sql, args...) if err != nil { return 0, err } - defer r.Close() sql, args, err = query. Select("id"). @@ -41,7 +46,12 @@ func GetUserID(db *pgxpool.Pool, ctx context.Context, key string) (int, error) { } var id int - err = c.QueryRow(ctx, sql, args...).Scan(&id) + err = tx.QueryRow(ctx, sql, args...).Scan(&id) + if err != nil { + return 0, err + } + + err = tx.Commit(ctx) if err != nil { return 0, err } diff --git a/api/core/administrator/id_test.go b/api/core/administrator/id_test.go new file mode 100644 index 0000000..865533b --- /dev/null +++ b/api/core/administrator/id_test.go @@ -0,0 +1,50 @@ +package administrator_test + +import ( + "context" + "jokes-bapak2-api/core/administrator" + "testing" +) + +func TestGetUserID_Success(t *testing.T) { + t.Cleanup(func() { Flush() }) + + c, err := db.Acquire(context.Background()) + if err != nil { + t.Error("an error was thrown:", err) + } + defer c.Release() + + _, err = c.Exec( + context.Background(), + `INSERT INTO administrators (id, key, token, last_used) VALUES ($1, $2, $3, $4)`, + administratorsData..., + ) + if err != nil { + t.Error("an error was thrown:", err) + } + + id, err := administrator.GetUserID(db, context.Background(), "very secure") + if err != nil { + t.Error("an error was thrown:", err) + } + + if id != 1 { + t.Error("id is not correct, want: 1, got:", id) + } +} + +func TestGetUserID_Failed(t *testing.T) { + t.Cleanup(func() { Flush() }) + + c, err := db.Acquire(context.Background()) + if err != nil { + t.Error("an error was thrown:", err) + } + defer c.Release() + + id, err := administrator.GetUserID(db, context.Background(), "very secure") + if err == nil { + t.Error("an error was expected, got:", id) + } +} diff --git a/api/core/administrator/init_test.go b/api/core/administrator/init_test.go new file mode 100644 index 0000000..9f656b1 --- /dev/null +++ b/api/core/administrator/init_test.go @@ -0,0 +1,122 @@ +package administrator_test + +import ( + "context" + "os" + "testing" + "time" + + "github.com/jackc/pgx/v4/pgxpool" +) + +var db *pgxpool.Pool + +var administratorsData = []interface{}{ + 1, "very secure", "not the real one", time.Now().Format(time.RFC3339), +} + +func TestMain(m *testing.M) { + defer Teardown() + Setup() + + os.Exit(m.Run()) +} + +func Setup() { + poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) + if err != nil { + panic(err) + } + + db, err = pgxpool.ConnectConfig(context.Background(), poolConfig) + if err != nil { + panic(err) + } + + conn, err := db.Acquire(context.Background()) + if err != nil { + panic(err) + } + defer conn.Release() + + tx, err := conn.Begin(context.Background()) + if err != nil { + panic(err) + } + defer tx.Rollback(context.Background()) + + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS submission") + if err != nil { + panic(err) + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS jokesbapak2") + if err != nil { + panic(err) + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS administrators") + if err != nil { + panic(err) + } + + _, err = tx.Exec( + context.Background(), + `CREATE TABLE IF NOT EXISTS administrators ( + id SERIAL PRIMARY KEY, + key VARCHAR(255) NOT NULL UNIQUE, + token TEXT, + last_used VARCHAR(255) + )`, + ) + if err != nil { + panic(err) + } + + err = tx.Commit(context.Background()) + if err != nil { + panic(err) + } +} + +func Teardown() (err error) { + tx, err := db.Begin(context.Background()) + if err != nil { + return err + } + defer tx.Rollback(context.Background()) + + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS submission") + if err != nil { + return err + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS jokesbapak2") + if err != nil { + return err + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS administrators") + if err != nil { + return err + } + + err = tx.Commit(context.Background()) + if err != nil { + return err + } + + db.Close() + return +} + +func Flush() error { + conn, err := db.Acquire(context.Background()) + if err != nil { + return err + } + defer conn.Release() + + _, err = conn.Exec(context.Background(), "TRUNCATE TABLE administrators;") + if err != nil { + return err + } + + return nil +} diff --git a/api/core/administrator/verify_test.go b/api/core/administrator/verify_test.go new file mode 100644 index 0000000..aab8821 --- /dev/null +++ b/api/core/administrator/verify_test.go @@ -0,0 +1,67 @@ +package administrator_test + +import ( + "context" + "jokes-bapak2-api/core/administrator" + "testing" +) + +func TestCheckKeyExists_Success(t *testing.T) { + t.Cleanup(func() { + Flush() + }) + + c, err := db.Acquire(context.Background()) + if err != nil { + t.Error("an error was thrown:", err) + } + defer c.Release() + + _, err = c.Exec( + context.Background(), + "INSERT INTO administrators (id, key, token, last_used) VALUES ($1, $2, $3, $4)", + administratorsData..., + ) + if err != nil { + t.Error("an error was thrown:", err) + } + + key, err := administrator.CheckKeyExists(db, context.Background(), "very secure") + if err != nil { + t.Error("an error was thrown:", err) + } + + if key != "not the real one" { + t.Error("key isn't not the real one, got:", key) + } +} + +func TestCheckKeyExists_Failing(t *testing.T) { + t.Cleanup(func() { + Flush() + }) + + c, err := db.Acquire(context.Background()) + if err != nil { + t.Error("an error was thrown:", err) + } + defer c.Release() + + _, err = c.Exec( + context.Background(), + "INSERT INTO administrators (id, key, token, last_used) VALUES ($1, $2, $3, $4)", + administratorsData..., + ) + if err != nil { + t.Error("an error was thrown:", err) + } + + key, err := administrator.CheckKeyExists(db, context.Background(), "others") + if err != nil { + t.Error("an error was thrown:", err) + } + + if key != "" { + t.Error("key is not empty, got:", key) + } +} diff --git a/api/core/joke/getter_test.go b/api/core/joke/getter_test.go index 0de67ae..07b31a1 100644 --- a/api/core/joke/getter_test.go +++ b/api/core/joke/getter_test.go @@ -11,7 +11,7 @@ import ( ) func TestGetAllJSONJokes(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) conn, err := db.Acquire(context.Background()) if err != nil { @@ -63,7 +63,8 @@ func TestGetAllJSONJokes(t *testing.T) { } func TestGetRandomJokeFromDB(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) + conn, err := db.Acquire(context.Background()) if err != nil { t.Error("an error was thrown:", err) @@ -114,7 +115,8 @@ func TestGetRandomJokeFromDB(t *testing.T) { } func TestGetRandomJokeFromCache(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) + jokes := []schema.Joke{ {ID: 1, Link: "link1", Creator: 1}, {ID: 2, Link: "link2", Creator: 1}, @@ -141,7 +143,7 @@ func TestGetRandomJokeFromCache(t *testing.T) { } func TestCheckJokesCache_True(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) jokes := []schema.Joke{ {ID: 1, Link: "link1", Creator: 1}, @@ -169,7 +171,8 @@ func TestCheckJokesCache_True(t *testing.T) { } func TestCheckJokesCache_False(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) + j, err := joke.CheckJokesCache(memory) if err != nil { t.Error("an error was thrown:", err) @@ -181,7 +184,7 @@ func TestCheckJokesCache_False(t *testing.T) { } func TestCheckTotalJokesCache_True(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) err := memory.Set("total", []byte("10")) if err != nil { @@ -199,7 +202,8 @@ func TestCheckTotalJokesCache_True(t *testing.T) { } func TestCheckTotalJokesCache_False(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) + j, err := joke.CheckTotalJokesCache(memory) if err != nil { t.Error("an error was thrown:", err) @@ -211,7 +215,7 @@ func TestCheckTotalJokesCache_False(t *testing.T) { } func TestGetCachedJokeByID(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) jokes := []schema.Joke{ {ID: 1, Link: "link1", Creator: 1}, @@ -248,7 +252,7 @@ func TestGetCachedJokeByID(t *testing.T) { } func TestGetCachedTotalJokes(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) err := memory.Set("total", []byte("10")) if err != nil { @@ -266,7 +270,8 @@ func TestGetCachedTotalJokes(t *testing.T) { } func TestCheckJokeExists(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) + conn, err := db.Acquire(context.Background()) if err != nil { t.Error("an error was thrown:", err) diff --git a/api/core/joke/init_test.go b/api/core/joke/init_test.go index 6796606..fbcfd64 100644 --- a/api/core/joke/init_test.go +++ b/api/core/joke/init_test.go @@ -20,17 +20,13 @@ var jokesData = []interface{}{ 2, "https://via.placeholder.com/300/07f/fff.png", 1, 3, "https://via.placeholder.com/300/08f/fff.png", 1, } -var submissionData = []interface{}{ - 1, "https://via.placeholder.com/300/01f/fff.png", "2021-08-03T18:20:38Z", "Test ", 0, - 2, "https://via.placeholder.com/300/02f/fff.png", "2021-08-04T18:20:38Z", "Test ", 1, -} var administratorsData = []interface{}{ 1, "very secure", "not the real one", time.Now().Format(time.RFC3339), 2, "test", "$argon2id$v=19$m=65536,t=16,p=4$3a08c79fbf2222467a623df9a9ebf75802c65a4f9be36eb1df2f5d2052d53cb7$ce434bd38f7ba1fc1f2eb773afb8a1f7f2dad49140803ac6cb9d7256ce9826fb3b4afa1e2488da511c852fc6c33a76d5657eba6298a8e49d617b9972645b7106", "", } func TestMain(m *testing.M) { - Setup() defer Teardown() + Setup() os.Exit(m.Run()) } @@ -70,6 +66,20 @@ func Setup() { } defer tx.Rollback(context.Background()) + // Dropping all table first + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS submission") + if err != nil { + panic(err) + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS jokesbapak2") + if err != nil { + panic(err) + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS administrators") + if err != nil { + panic(err) + } + _, err = tx.Exec( context.Background(), `CREATE TABLE IF NOT EXISTS administrators ( @@ -85,7 +95,7 @@ func Setup() { _, err = tx.Exec( context.Background(), `CREATE TABLE IF NOT EXISTS jokesbapak2 ( - id SERIAL PRIMARY KEY, + id SERIAL PRIMARY KEY, link TEXT UNIQUE, creator INT NOT NULL REFERENCES "administrators" ("id") );`, @@ -97,7 +107,7 @@ func Setup() { context.Background(), `CREATE TABLE IF NOT EXISTS submission ( id SERIAL PRIMARY KEY, - link UNIQUE NOT NULL, + link VARCHAR(255) UNIQUE NOT NULL, created_at VARCHAR(255), author VARCHAR(255) NOT NULL, status SMALLINT DEFAULT 0 @@ -114,7 +124,32 @@ func Setup() { } func Teardown() (err error) { + tx, err := db.Begin(context.Background()) + if err != nil { + return err + } + defer tx.Rollback(context.Background()) + + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS submission") + if err != nil { + return err + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS jokesbapak2") + if err != nil { + return err + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS administrators") + if err != nil { + return err + } + + err = tx.Commit(context.Background()) + if err != nil { + return err + } + db.Close() + err = cache.Close() if err != nil { return @@ -123,33 +158,22 @@ func Teardown() (err error) { return } -func TruncateTable(db *pgxpool.Pool, cache *redis.Client, memory *bigcache.BigCache) error { +func Flush() error { conn, err := db.Acquire(context.Background()) if err != nil { return err } defer conn.Release() - tx, err := conn.Begin(context.Background()) + _, err = conn.Exec(context.Background(), "TRUNCATE TABLE submission;") if err != nil { return err } - defer tx.Rollback(context.Background()) - - _, err = tx.Exec(context.Background(), "TRUNCATE TABLE submission;") + _, err = conn.Exec(context.Background(), "TRUNCATE TABLE jokesbapak2;") if err != nil { return err } - _, err = tx.Exec(context.Background(), "TRUNCATE TABLE jokesbapak2;") - if err != nil { - return err - } - _, err = tx.Exec(context.Background(), "TRUNCATE TABLE administrators;") - if err != nil { - return err - } - - err = tx.Commit(context.Background()) + _, err = conn.Exec(context.Background(), "TRUNCATE TABLE administrators;") if err != nil { return err } diff --git a/api/core/joke/setter_test.go b/api/core/joke/setter_test.go index c4dc887..e1b0e62 100644 --- a/api/core/joke/setter_test.go +++ b/api/core/joke/setter_test.go @@ -10,7 +10,7 @@ import ( ) func TestSetAllJSONJoke(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) conn, err := db.Acquire(context.Background()) if err != nil { @@ -58,7 +58,7 @@ func TestSetAllJSONJoke(t *testing.T) { } func TestSetTotalJoke(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) conn, err := db.Acquire(context.Background()) if err != nil { @@ -106,7 +106,7 @@ func TestSetTotalJoke(t *testing.T) { } func TestInsertJokeIntoDB(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) data := schema.Joke{ ID: 1, @@ -120,7 +120,7 @@ func TestInsertJokeIntoDB(t *testing.T) { } func TestDeleteSingleJoke(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) conn, err := db.Acquire(context.Background()) if err != nil { @@ -168,7 +168,7 @@ func TestDeleteSingleJoke(t *testing.T) { } func TestUpdateJoke(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) conn, err := db.Acquire(context.Background()) if err != nil { diff --git a/api/core/submit/getter.go b/api/core/submit/getter.go index 3345c21..6ed653f 100644 --- a/api/core/submit/getter.go +++ b/api/core/submit/getter.go @@ -1,11 +1,11 @@ package submit import ( - "bytes" "context" "jokes-bapak2-api/core/schema" "net/url" "strconv" + "strings" "github.com/aldy505/bob" "github.com/georgysavva/scany/pgxscan" @@ -82,7 +82,8 @@ func GetSubmittedItems(db *pgxpool.Pool, ctx context.Context, queries schema.Sub func GetterQueryBuilder(queries schema.SubmissionQuery, status, limit, offset int) (string, []interface{}, error) { var sql string var args []interface{} - var sqlQuery *bytes.Buffer = &bytes.Buffer{} + var sqlQuery strings.Builder + sqlQuery.WriteString("SELECT * FROM submission WHERE TRUE") if queries.Author != "" { diff --git a/api/core/submit/getter_test.go b/api/core/submit/getter_test.go new file mode 100644 index 0000000..d033fd8 --- /dev/null +++ b/api/core/submit/getter_test.go @@ -0,0 +1,64 @@ +package submit_test + +import ( + "context" + "jokes-bapak2-api/core/schema" + "jokes-bapak2-api/core/submit" + "testing" +) + +func TestGetSubmittedItems(t *testing.T) { + defer Flush() + + c, err := db.Acquire(context.Background()) + if err != nil { + t.Error("an error was thrown:", err) + } + defer c.Release() + + _, err = c.Exec(context.Background(), "INSERT INTO submission (id, link, created_at, author, status) VALUES ($1, $2, $3, $4, $5), ($6, $7, $8, $9, $10)", submissionData...) + if err != nil { + t.Error("an error was thrown:", err) + } + + items, err := submit.GetSubmittedItems(db, context.Background(), schema.SubmissionQuery{}) + if err != nil { + t.Error("an error was thrown:", err) + } + + if len(items) != 2 { + t.Error("expected 2 items, got", len(items)) + } +} + +func TestGetterQueryBuilder(t *testing.T) { + s, _, err := submit.GetterQueryBuilder(schema.SubmissionQuery{}, 0, 0, 0) + if err != nil { + t.Error("an error was thrown:", err) + } + + if s != "SELECT * FROM submission WHERE TRUE LIMIT 20" { + t.Error("expected query to be", "SELECT * FROM submission WHERE TRUE LIMIT 20", "got", s) + } + + s, i, err := submit.GetterQueryBuilder(schema.SubmissionQuery{ + Author: "Test ", + Approved: "true", + Page: "2", + }, 2, 15, 10) + if err != nil { + t.Error("an error was thrown:", err) + } + + if s != "SELECT * FROM submission WHERE TRUE AND author = $1 AND status = $2 LIMIT 15 OFFSET 10" { + t.Error("expected query to be", "SELECT * FROM submission WHERE TRUE AND author = $1 AND status = $2 LIMIT 15 OFFSET 15", "got:", s) + } + + if i[0].(string) != "Test " { + t.Error("expected first arg to be Test , got:", i[0].(string)) + } + + if i[1].(int) != 1 { + t.Error("expected second arg to be 1, got:", i[1].(int)) + } +} diff --git a/api/core/submit/init_test.go b/api/core/submit/init_test.go new file mode 100644 index 0000000..d92d5f1 --- /dev/null +++ b/api/core/submit/init_test.go @@ -0,0 +1,123 @@ +package submit_test + +import ( + "context" + "os" + "testing" + + "github.com/jackc/pgx/v4/pgxpool" +) + +var db *pgxpool.Pool + +var submissionData = []interface{}{ + 1, "https://via.placeholder.com/300/01f/fff.png", "2021-08-03T18:20:38Z", "Test ", 0, + 2, "https://via.placeholder.com/300/02f/fff.png", "2021-08-04T18:20:38Z", "Test ", 1, +} + +func TestMain(m *testing.M) { + defer Teardown() + Setup() + + os.Exit(m.Run()) +} + +func Setup() { + poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) + if err != nil { + panic(err) + } + + db, err = pgxpool.ConnectConfig(context.Background(), poolConfig) + if err != nil { + panic(err) + } + + conn, err := db.Acquire(context.Background()) + if err != nil { + panic(err) + } + defer conn.Release() + + tx, err := conn.Begin(context.Background()) + if err != nil { + panic(err) + } + defer tx.Rollback(context.Background()) + + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS submission") + if err != nil { + panic(err) + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS jokesbapak2") + if err != nil { + panic(err) + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS administrators") + if err != nil { + panic(err) + } + + _, err = tx.Exec( + context.Background(), + `CREATE TABLE IF NOT EXISTS submission ( + id SERIAL PRIMARY KEY, + link VARCHAR(255) UNIQUE NOT NULL, + created_at VARCHAR(255), + author VARCHAR(255) NOT NULL, + status SMALLINT DEFAULT 0 + );`, + ) + if err != nil { + panic(err) + } + + err = tx.Commit(context.Background()) + if err != nil { + panic(err) + } +} + +func Teardown() (err error) { + tx, err := db.Begin(context.Background()) + if err != nil { + return err + } + defer tx.Rollback(context.Background()) + + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS submission") + if err != nil { + return err + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS jokesbapak2") + if err != nil { + return err + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS administrators") + if err != nil { + return err + } + + err = tx.Commit(context.Background()) + if err != nil { + return err + } + + db.Close() + return +} + +func Flush() error { + conn, err := db.Acquire(context.Background()) + if err != nil { + return err + } + defer conn.Release() + + _, err = conn.Exec(context.Background(), "TRUNCATE TABLE submission") + if err != nil { + return err + } + + return nil +} diff --git a/api/core/submit/setter_test.go b/api/core/submit/setter_test.go new file mode 100644 index 0000000..31c59bb --- /dev/null +++ b/api/core/submit/setter_test.go @@ -0,0 +1,21 @@ +package submit_test + +import ( + "context" + "jokes-bapak2-api/core/schema" + "jokes-bapak2-api/core/submit" + "testing" +) + +func TestSubmitJoke(t *testing.T) { + defer Flush() + + s, err := submit.SubmitJoke(db, context.Background(), schema.Submission{Author: "Test "}, "https://example.net/img.png") + if err != nil { + t.Error("an error was thrown:", err) + } + + if s.Link != "https://example.net/img.png" { + t.Error("link is not correct, got:", s.Link) + } +}