335 lines
8.0 KiB
Go
335 lines
8.0 KiB
Go
package sqlite
|
|
|
|
import (
|
|
"database/sql"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
|
"git.warky.dev/wdevs/relspecgo/pkg/readers"
|
|
)
|
|
|
|
// setupTestDatabase creates a temporary SQLite database with test data
|
|
func setupTestDatabase(t *testing.T) string {
|
|
tmpDir := t.TempDir()
|
|
dbPath := filepath.Join(tmpDir, "test.db")
|
|
|
|
db, err := sql.Open("sqlite", dbPath)
|
|
require.NoError(t, err)
|
|
defer db.Close()
|
|
|
|
// Create test schema
|
|
schema := `
|
|
PRAGMA foreign_keys = ON;
|
|
|
|
CREATE TABLE users (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
username VARCHAR(50) NOT NULL UNIQUE,
|
|
email VARCHAR(100) NOT NULL,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE posts (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL,
|
|
title VARCHAR(200) NOT NULL,
|
|
content TEXT,
|
|
published BOOLEAN DEFAULT 0,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE comments (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
post_id INTEGER NOT NULL,
|
|
user_id INTEGER NOT NULL,
|
|
comment TEXT NOT NULL,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE INDEX idx_posts_user_id ON posts(user_id);
|
|
CREATE INDEX idx_comments_post_id ON comments(post_id);
|
|
CREATE UNIQUE INDEX idx_users_email ON users(email);
|
|
|
|
CREATE VIEW user_post_count AS
|
|
SELECT u.id, u.username, COUNT(p.id) as post_count
|
|
FROM users u
|
|
LEFT JOIN posts p ON u.id = p.user_id
|
|
GROUP BY u.id, u.username;
|
|
`
|
|
|
|
_, err = db.Exec(schema)
|
|
require.NoError(t, err)
|
|
|
|
return dbPath
|
|
}
|
|
|
|
func TestReader_ReadDatabase(t *testing.T) {
|
|
dbPath := setupTestDatabase(t)
|
|
defer os.Remove(dbPath)
|
|
|
|
options := &readers.ReaderOptions{
|
|
FilePath: dbPath,
|
|
}
|
|
|
|
reader := NewReader(options)
|
|
db, err := reader.ReadDatabase()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, db)
|
|
|
|
// Check database metadata
|
|
assert.Equal(t, "test.db", db.Name)
|
|
assert.Equal(t, models.SqlLiteDatabaseType, db.DatabaseType)
|
|
assert.Equal(t, "sqlite", db.SourceFormat)
|
|
assert.NotEmpty(t, db.DatabaseVersion)
|
|
|
|
// Check schemas (SQLite should have a single "main" schema)
|
|
require.Len(t, db.Schemas, 1)
|
|
schema := db.Schemas[0]
|
|
assert.Equal(t, "main", schema.Name)
|
|
|
|
// Check tables
|
|
assert.Len(t, schema.Tables, 3)
|
|
tableNames := make([]string, len(schema.Tables))
|
|
for i, table := range schema.Tables {
|
|
tableNames[i] = table.Name
|
|
}
|
|
assert.Contains(t, tableNames, "users")
|
|
assert.Contains(t, tableNames, "posts")
|
|
assert.Contains(t, tableNames, "comments")
|
|
|
|
// Check views
|
|
assert.Len(t, schema.Views, 1)
|
|
assert.Equal(t, "user_post_count", schema.Views[0].Name)
|
|
assert.NotEmpty(t, schema.Views[0].Definition)
|
|
}
|
|
|
|
func TestReader_ReadTable_Users(t *testing.T) {
|
|
dbPath := setupTestDatabase(t)
|
|
defer os.Remove(dbPath)
|
|
|
|
options := &readers.ReaderOptions{
|
|
FilePath: dbPath,
|
|
}
|
|
|
|
reader := NewReader(options)
|
|
db, err := reader.ReadDatabase()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, db)
|
|
|
|
// Find users table
|
|
var usersTable *models.Table
|
|
for _, table := range db.Schemas[0].Tables {
|
|
if table.Name == "users" {
|
|
usersTable = table
|
|
break
|
|
}
|
|
}
|
|
|
|
require.NotNil(t, usersTable)
|
|
assert.Equal(t, "users", usersTable.Name)
|
|
assert.Equal(t, "main", usersTable.Schema)
|
|
|
|
// Check columns
|
|
assert.Len(t, usersTable.Columns, 4)
|
|
|
|
// Check id column
|
|
idCol, exists := usersTable.Columns["id"]
|
|
require.True(t, exists)
|
|
assert.Equal(t, "int", idCol.Type)
|
|
assert.True(t, idCol.IsPrimaryKey)
|
|
assert.True(t, idCol.AutoIncrement)
|
|
assert.True(t, idCol.NotNull)
|
|
|
|
// Check username column
|
|
usernameCol, exists := usersTable.Columns["username"]
|
|
require.True(t, exists)
|
|
assert.Equal(t, "string", usernameCol.Type)
|
|
assert.True(t, usernameCol.NotNull)
|
|
assert.False(t, usernameCol.IsPrimaryKey)
|
|
|
|
// Check email column
|
|
emailCol, exists := usersTable.Columns["email"]
|
|
require.True(t, exists)
|
|
assert.Equal(t, "string", emailCol.Type)
|
|
assert.True(t, emailCol.NotNull)
|
|
|
|
// Check primary key constraint
|
|
assert.Len(t, usersTable.Constraints, 1)
|
|
pkConstraint, exists := usersTable.Constraints["users_pkey"]
|
|
require.True(t, exists)
|
|
assert.Equal(t, models.PrimaryKeyConstraint, pkConstraint.Type)
|
|
assert.Equal(t, []string{"id"}, pkConstraint.Columns)
|
|
|
|
// Check indexes (should have unique index on email and username)
|
|
assert.GreaterOrEqual(t, len(usersTable.Indexes), 1)
|
|
}
|
|
|
|
func TestReader_ReadTable_Posts(t *testing.T) {
|
|
dbPath := setupTestDatabase(t)
|
|
defer os.Remove(dbPath)
|
|
|
|
options := &readers.ReaderOptions{
|
|
FilePath: dbPath,
|
|
}
|
|
|
|
reader := NewReader(options)
|
|
db, err := reader.ReadDatabase()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, db)
|
|
|
|
// Find posts table
|
|
var postsTable *models.Table
|
|
for _, table := range db.Schemas[0].Tables {
|
|
if table.Name == "posts" {
|
|
postsTable = table
|
|
break
|
|
}
|
|
}
|
|
|
|
require.NotNil(t, postsTable)
|
|
|
|
// Check columns
|
|
assert.Len(t, postsTable.Columns, 6)
|
|
|
|
// Check foreign key constraint
|
|
hasForeignKey := false
|
|
for _, constraint := range postsTable.Constraints {
|
|
if constraint.Type == models.ForeignKeyConstraint {
|
|
hasForeignKey = true
|
|
assert.Equal(t, "users", constraint.ReferencedTable)
|
|
assert.Equal(t, "CASCADE", constraint.OnDelete)
|
|
}
|
|
}
|
|
assert.True(t, hasForeignKey, "Posts table should have a foreign key constraint")
|
|
|
|
// Check relationships
|
|
assert.GreaterOrEqual(t, len(postsTable.Relationships), 1)
|
|
|
|
// Check indexes
|
|
hasUserIdIndex := false
|
|
for _, index := range postsTable.Indexes {
|
|
if index.Name == "idx_posts_user_id" {
|
|
hasUserIdIndex = true
|
|
assert.Contains(t, index.Columns, "user_id")
|
|
}
|
|
}
|
|
assert.True(t, hasUserIdIndex, "Posts table should have idx_posts_user_id index")
|
|
}
|
|
|
|
func TestReader_ReadTable_Comments(t *testing.T) {
|
|
dbPath := setupTestDatabase(t)
|
|
defer os.Remove(dbPath)
|
|
|
|
options := &readers.ReaderOptions{
|
|
FilePath: dbPath,
|
|
}
|
|
|
|
reader := NewReader(options)
|
|
db, err := reader.ReadDatabase()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, db)
|
|
|
|
// Find comments table
|
|
var commentsTable *models.Table
|
|
for _, table := range db.Schemas[0].Tables {
|
|
if table.Name == "comments" {
|
|
commentsTable = table
|
|
break
|
|
}
|
|
}
|
|
|
|
require.NotNil(t, commentsTable)
|
|
|
|
// Check foreign key constraints (should have 2)
|
|
fkCount := 0
|
|
for _, constraint := range commentsTable.Constraints {
|
|
if constraint.Type == models.ForeignKeyConstraint {
|
|
fkCount++
|
|
}
|
|
}
|
|
assert.Equal(t, 2, fkCount, "Comments table should have 2 foreign key constraints")
|
|
}
|
|
|
|
func TestReader_ReadSchema(t *testing.T) {
|
|
dbPath := setupTestDatabase(t)
|
|
defer os.Remove(dbPath)
|
|
|
|
options := &readers.ReaderOptions{
|
|
FilePath: dbPath,
|
|
}
|
|
|
|
reader := NewReader(options)
|
|
schema, err := reader.ReadSchema()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, schema)
|
|
assert.Equal(t, "main", schema.Name)
|
|
assert.Len(t, schema.Tables, 3)
|
|
assert.Len(t, schema.Views, 1)
|
|
}
|
|
|
|
func TestReader_ReadTable(t *testing.T) {
|
|
dbPath := setupTestDatabase(t)
|
|
defer os.Remove(dbPath)
|
|
|
|
options := &readers.ReaderOptions{
|
|
FilePath: dbPath,
|
|
}
|
|
|
|
reader := NewReader(options)
|
|
table, err := reader.ReadTable()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, table)
|
|
assert.NotEmpty(t, table.Name)
|
|
assert.NotEmpty(t, table.Columns)
|
|
}
|
|
|
|
func TestReader_ConnectionString(t *testing.T) {
|
|
dbPath := setupTestDatabase(t)
|
|
defer os.Remove(dbPath)
|
|
|
|
options := &readers.ReaderOptions{
|
|
ConnectionString: dbPath,
|
|
}
|
|
|
|
reader := NewReader(options)
|
|
db, err := reader.ReadDatabase()
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, db)
|
|
assert.Len(t, db.Schemas, 1)
|
|
}
|
|
|
|
func TestReader_InvalidPath(t *testing.T) {
|
|
options := &readers.ReaderOptions{
|
|
FilePath: "/nonexistent/path/to/database.db",
|
|
}
|
|
|
|
reader := NewReader(options)
|
|
_, err := reader.ReadDatabase()
|
|
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestReader_MissingPath(t *testing.T) {
|
|
options := &readers.ReaderOptions{}
|
|
|
|
reader := NewReader(options)
|
|
_, err := reader.ReadDatabase()
|
|
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "file path or connection string is required")
|
|
}
|