feat: ✨ Added Sqlite reader
This commit is contained in:
334
pkg/readers/sqlite/reader_test.go
Normal file
334
pkg/readers/sqlite/reader_test.go
Normal file
@@ -0,0 +1,334 @@
|
||||
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")
|
||||
}
|
||||
Reference in New Issue
Block a user