Skip to content
Snippets Groups Projects
db_test.go 4.48 KiB
Newer Older
gbe's avatar
gbe committed
package main

import (
	"context"
	"testing"

	"github.com/jmoiron/sqlx"
	_ "modernc.org/ql/driver"
)

func assertNoError(t *testing.T, err error) {
	if err != nil {
		t.Fatalf("unexpected error: %s", err)
	}
}

func expectNoError(t *testing.T, err error) {
	if err != nil {
		t.Errorf("unexpected error: %s", err)
	}
}

func expectError(t *testing.T, err error) {
	if err == nil {
		t.Error("expected an error")
	}
}

func TestInitSQLdb(t *testing.T) {
	ctx := context.Background()

	db, err := sqlx.Open("ql-mem", "unit-test")
	assertNoError(t, err)
	defer db.Close()

	// Try to init the DB twice. No call should error, and we should have a migrations
	// table afterwards.

gbe's avatar
gbe committed
	err = initDB(ctx, db)
gbe's avatar
gbe committed
	assertNoError(t, err)

gbe's avatar
gbe committed
	err = initDB(ctx, db)
gbe's avatar
gbe committed
	assertNoError(t, err)

	// Assert that there is one migration
	var count int
	err = db.Get(&count, `SELECT count(*) FROM migrations`)
	assertNoError(t, err)

gbe's avatar
gbe committed
	if count != 3 {
		t.Errorf("unexpected number of applied migrations: %d, want 3", count)
gbe's avatar
gbe committed
	}

	// Try to insert the same migration twice, ensure that the second insert fails
	tx, err := db.Beginx()
	assertNoError(t, err)

	_, err = tx.ExecContext(ctx, `INSERT INTO migrations VALUES (?1)`, "test")
	assertNoError(t, err)

	err = tx.Commit()
	assertNoError(t, err)

	tx, err = db.Beginx()
	assertNoError(t, err)

	_, err = tx.ExecContext(ctx, `INSERT INTO migrations VALUES (?1)`, "test")
	if err == nil {
		t.Error("expected an error, got nil")
	}

	err = tx.Rollback()
	assertNoError(t, err)
}

func TestInitialDBStructure(t *testing.T) {
	ctx := context.Background()

	db, err := sqlx.Open("ql-mem", "unit-test")
	assertNoError(t, err)
	defer db.Close()

	// Try to init the DB twice. No call should error, and we should have a migrations
	// table afterwards.

gbe's avatar
gbe committed
	err = initDB(ctx, db)
gbe's avatar
gbe committed
	assertNoError(t, err)

	// Run a few tests on the DB structure:
	t.Run("wines-country", func(t *testing.T) {
		// This tests the "country" column on wines
		tx, err := db.Begin()
		assertNoError(t, err)
		defer tx.Rollback() //nolint:errcheck
gbe's avatar
gbe committed

		// - 2 characters
		_, err = tx.Exec(`INSERT INTO wines (name, country) VALUES (?1, ?2)`, "test-wine", "DE")
		expectNoError(t, err)

		// - more than 2 characters
		_, err = tx.Exec(`INSERT INTO wines (name, country) VALUES (?1, ?2)`, "test-wine", "foobar")
		expectError(t, err)

		// - less than 2 characters
		_, err = tx.Exec(`INSERT INTO wines (name, country) VALUES (?1, ?2)`, "test-wine", "a")
		expectError(t, err)

		// - null
		_, err = tx.Exec(`INSERT INTO wines (name) VALUES (?1)`, "test-wine")
		expectNoError(t, err)
	})

	t.Run("wine-comment", func(t *testing.T) {
		// This test ensures that to insert a comment, there has to be a matching wine entry
		tx, err := db.Begin()
		assertNoError(t, err)
		defer tx.Rollback() //nolint:errcheck
gbe's avatar
gbe committed

		res, err := tx.ExecContext(ctx, `INSERT INTO wines (name) VALUES (?1)`, "test-wine")
		assertNoError(t, err)

		id, err := res.LastInsertId()
		assertNoError(t, err)

		if id == 0 {
			t.Fatal("unexpected insert id, want anything but 0")
		}

		// - insert a comment with no existing wine
		_, err = tx.ExecContext(ctx, `INSERT INTO comments (content, wine) VALUES(?1, ?2)`, "test!", id+1)
		if err == nil {
			t.Error("expected error when adding comment without wine")
		}

		// - insert comment for the test wine
		_, err = tx.ExecContext(ctx, `INSERT INTO comments (content, wine) VALUES(?1, ?2)`, "test!", id)
		assertNoError(t, err)

		// - delete test wine
		_, err = tx.ExecContext(ctx, `DELETE FROM wines`)
		assertNoError(t, err) // Actually, I want this to error since there's a comment that references this wine.

		// - delete test comment
		_, err = tx.ExecContext(ctx, `DELETE FROM comments`)
		assertNoError(t, err)
	})

	t.Run("state", func(t *testing.T) {
		tx, err := db.Begin()
		assertNoError(t, err)
		defer tx.Rollback() //nolint:errcheck
gbe's avatar
gbe committed

		// Insert the same key twice
		_, err = tx.Exec(`INSERT INTO state (key, val) VALUES (?1, ?2)`, "test", "fnord")
		expectNoError(t, err)

		_, err = tx.Exec(`INSERT INTO state (key, val) VALUES (?1, ?2)`, "test", "fnord")
		expectError(t, err)
	})

	t.Run("users", func(t *testing.T) {
		// This test ensures that to insert a comment, there has to be a matching wine entry
		tx, err := db.Begin()
		assertNoError(t, err)
		defer tx.Rollback() //nolint:errcheck
gbe's avatar
gbe committed

		// Insert the same user twice
		_, err = tx.Exec(`INSERT INTO users (name, password) VALUES (?1, ?2)`, "test", "fnord")
		expectNoError(t, err)

		_, err = tx.Exec(`INSERT INTO users (name, password) VALUES (?1, ?2)`, "test", "fnord")
		expectError(t, err)
	})
}