From c01d2a519678f751e77c255c3f1f8cbd7792ffbd Mon Sep 17 00:00:00 2001 From: Akisolu Date: Mon, 10 Aug 2026 23:05:56 -0400 Subject: [PATCH 1/6] fix: add default in feedback table --- schema.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/schema.sql b/schema.sql index 9c8b50b..89ad7fc 100644 --- a/schema.sql +++ b/schema.sql @@ -15,7 +15,7 @@ CREATE TABLE IF NOT EXISTS states ( CREATE TABLE IF NOT EXISTS feedbacks ( feedback_id SERIAL PRIMARY KEY, message TEXT NOT NULL, - state_id INT NOT NULL, + state_id INT NOT NULL DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); @@ -95,4 +95,4 @@ VALUES ('read'), ('archived'), ('deleted') -ON CONFLICT (state_id) DO NOTHING; \ No newline at end of file +ON CONFLICT (id_state) DO NOTHING; \ No newline at end of file From 00f3f374cd9d58576ec4c75ad183634ab9510c0c Mon Sep 17 00:00:00 2001 From: Akisolu Date: Mon, 10 Aug 2026 23:06:09 -0400 Subject: [PATCH 2/6] fix: add default in feedback table --- schema.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schema.sql b/schema.sql index 89ad7fc..0efcae2 100644 --- a/schema.sql +++ b/schema.sql @@ -95,4 +95,4 @@ VALUES ('read'), ('archived'), ('deleted') -ON CONFLICT (id_state) DO NOTHING; \ No newline at end of file +ON CONFLICT (name) DO NOTHING; \ No newline at end of file From ae12576c6254d757f5fb589160740bbc11000faf Mon Sep 17 00:00:00 2001 From: Akisolu Date: Mon, 10 Aug 2026 23:12:06 -0400 Subject: [PATCH 3/6] fix: add index to database --- schema.sql | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/schema.sql b/schema.sql index 0efcae2..4cb6017 100644 --- a/schema.sql +++ b/schema.sql @@ -13,7 +13,7 @@ CREATE TABLE IF NOT EXISTS states ( ); CREATE TABLE IF NOT EXISTS feedbacks ( - feedback_id SERIAL PRIMARY KEY, + feedback_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), message TEXT NOT NULL, state_id INT NOT NULL DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP @@ -95,4 +95,9 @@ VALUES ('read'), ('archived'), ('deleted') -ON CONFLICT (name) DO NOTHING; \ No newline at end of file +ON CONFLICT (name) DO NOTHING; + +-- 5. INDEX + +CREATE INDEX IF NOT EXISTS idx_feedbacks_state_id ON feedbacks(state_id); +CREATE INDEX IF NOT EXISTS idx_feedback_records_feedback_id ON feedback_records(feedback_id); \ No newline at end of file From 8a17f5683053fdedcde514a1c5d76848b63dae1d Mon Sep 17 00:00:00 2001 From: Akisolu Date: Mon, 10 Aug 2026 23:50:38 -0400 Subject: [PATCH 4/6] feat(database): add PostgreSQL schema migration, trigger audit, and integration test --- .github/workflows/ci.yml | 5 +- schema.sql | 6 ++- tests/Integration/FeedbackMigrationTest.php | 52 +++++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 tests/Integration/FeedbackMigrationTest.php diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ab0865..4795b71 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,6 +85,9 @@ jobs: echo "REDIS_PORT=6379" >> .env echo "RATE_LIMIT_MAX_REQUESTS=10" >> .env echo "RATE_LIMIT_DECAY=600" >> .env - + + - name: Run Database Migrations + run: php bin/migrate.php + - name: Run PHPUnit Tests run: vendor/bin/phpunit \ No newline at end of file diff --git a/schema.sql b/schema.sql index 4cb6017..4625971 100644 --- a/schema.sql +++ b/schema.sql @@ -13,7 +13,7 @@ CREATE TABLE IF NOT EXISTS states ( ); CREATE TABLE IF NOT EXISTS feedbacks ( - feedback_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + feedback_id SERIAL PRIMARY KEY, message TEXT NOT NULL, state_id INT NOT NULL DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP @@ -97,6 +97,10 @@ VALUES ('deleted') ON CONFLICT (name) DO NOTHING; +INSERT INTO users (user_id, username, password_hash) +VALUES (1, 'system', '$2y$10$e.g.placeholder.hash.system.user') +ON CONFLICT (user_id) DO NOTHING; + -- 5. INDEX CREATE INDEX IF NOT EXISTS idx_feedbacks_state_id ON feedbacks(state_id); diff --git a/tests/Integration/FeedbackMigrationTest.php b/tests/Integration/FeedbackMigrationTest.php new file mode 100644 index 0000000..cd20aee --- /dev/null +++ b/tests/Integration/FeedbackMigrationTest.php @@ -0,0 +1,52 @@ +pdo = $container->get(PDO::class); + } + + public function test_trigger_records_state_change_automatically(): void + { + // 1. Insert anonymous feedback + $stmt = $this->pdo->prepare("INSERT INTO feedbacks (message) VALUES (:msg) RETURNING feedback_id"); + $stmt->execute(['msg' => 'TDD test feedback']); + $feedbackId = (int) $stmt->fetchColumn(); + + $this->assertGreaterThan(0, $feedbackId); + + // 2. Simulate an authenticated user in the app session in Postgres + $this->pdo->exec("SET LOCAL app.current_user_id = '1'"); + + // 3. Change the state from 1 (unread) to 2 (read) + $updateStmt = $this->pdo->prepare("UPDATE feedbacks SET state_id = 2 WHERE feedback_id = :id"); + $updateStmt->execute(['id' => $feedbackId]); + + // 4. Verify that the TRIGGER created the audit log in feedback_records + $auditStmt = $this->pdo->prepare("SELECT * FROM feedback_records WHERE feedback_id = :id"); + $auditStmt->execute(['id' => $feedbackId]); + $record = $auditStmt->fetch(PDO::FETCH_ASSOC); + + $this->assertNotEmpty($record); + $this->assertEquals(1, $record['user_id']); + $this->assertEquals(1, $record['old_state_id']); + $this->assertEquals(2, $record['new_state_id']); + + // Clean + $this->pdo->prepare("DELETE FROM feedbacks WHERE feedback_id = :id")->execute(['id' => $feedbackId]); + } +} \ No newline at end of file From 9c9bf1e7266275ce85cfeed484e4b4e50ead49d9 Mon Sep 17 00:00:00 2001 From: Akisolu Date: Tue, 11 Aug 2026 00:08:16 -0400 Subject: [PATCH 5/6] feat: add migration --- bin/migrate.php | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 bin/migrate.php diff --git a/bin/migrate.php b/bin/migrate.php new file mode 100644 index 0000000..79f412c --- /dev/null +++ b/bin/migrate.php @@ -0,0 +1,40 @@ +get(PDO::class); + +$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +$sqlPath = __DIR__ . '/../schema.sql'; + +if (!file_exists($sqlPath)) { + echo "\033[31m[ERROR] No se encontró el archivo de migración en: {$sqlPath}\033[0m\n"; + exit(1); +} + +echo "\033[33mEjecutando migración en PostgreSQL...\033[0m\n"; + +try { + $sql = file_get_contents($sqlPath); + + // Ejecutar el SQL completo dentro de una transacción explícita + $pdo->beginTransaction(); + $pdo->exec($sql); + $pdo->commit(); + + echo "\033[32m[ÉXITO] ¡Todas las tablas, índices, triggers y registros iniciales fueron creados exitosamente!\033[0m\n"; +} catch (\PDOException $e) { + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + echo "\033[31m[ERROR EN LA MIGRACIÓN]\033[0m\n"; + echo "Mensaje: " . $e->getMessage() . "\n"; + exit(1); +} \ No newline at end of file From 49b4ed3218dffd723acd7f84362a5dfd61117587 Mon Sep 17 00:00:00 2001 From: Akisolu Date: Tue, 11 Aug 2026 00:12:11 -0400 Subject: [PATCH 6/6] chore: translate migrate.php to english --- bin/migrate.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bin/migrate.php b/bin/migrate.php index 79f412c..52b859a 100644 --- a/bin/migrate.php +++ b/bin/migrate.php @@ -15,26 +15,26 @@ $sqlPath = __DIR__ . '/../schema.sql'; if (!file_exists($sqlPath)) { - echo "\033[31m[ERROR] No se encontró el archivo de migración en: {$sqlPath}\033[0m\n"; + echo "\033[31m[ERROR] The migration file was not found in: {$sqlPath}\033[0m\n"; exit(1); } -echo "\033[33mEjecutando migración en PostgreSQL...\033[0m\n"; +echo "\033[33mRunning migration in PostgreSQL...\033[0m\n"; try { $sql = file_get_contents($sqlPath); - // Ejecutar el SQL completo dentro de una transacción explícita + // Execute the complete SQL within an explicit transaction $pdo->beginTransaction(); $pdo->exec($sql); $pdo->commit(); - echo "\033[32m[ÉXITO] ¡Todas las tablas, índices, triggers y registros iniciales fueron creados exitosamente!\033[0m\n"; + echo "\033[32m[SUCCESS] All tables, indexes, triggers, and initial records were successfully created!\033[0m\n"; } catch (\PDOException $e) { if ($pdo->inTransaction()) { $pdo->rollBack(); } - echo "\033[31m[ERROR EN LA MIGRACIÓN]\033[0m\n"; - echo "Mensaje: " . $e->getMessage() . "\n"; + echo "\033[31m[MIGRATION ERROR]\033[0m\n"; + echo "Message: " . $e->getMessage() . "\n"; exit(1); } \ No newline at end of file