Lifts role_templates / role_template_fields / user_role_assignments from fewo-webapp into oatpp-authkit, exposed via the declarative SchemaContract introduced in PR 0. New files (all in oatpp-authkit): - dto/RoleTemplateDto.hpp — RoleTemplateDto, RoleTemplateFieldDto, UserRoleAssignmentDto. UserWithPermissionsDto stays in fewo (fewo- specific /api/auth/me response shape). - db/RoleTemplateDb.hpp — DbClient with all queries (CRUD + cascade soft-delete + getEffectiveFieldPermissions). RoleTemplateSchema struct declares the three tables' columns/indexes/sidecar tables in the new declarative form. TemporalRepository overlays valid_until + the composite UNIQUE(entity_id, valid_until) index. - repo/ConcreteRoleTemplateRepository.hpp — Repository<RoleTemplateDto> inner adapter; makeRoleTemplateRepository helper composes the stack. - docs/MIGRATIONS.md — Atlas workflow for consumers (atlasgo.io as the diff-driven migration tool; SchemaBuilder produces desired state, Atlas generates versioned SQL, SchemaContract::verify asserts at runtime). - test/test_role_template_schema.cpp — verifies SchemaBuilder< RoleTemplateSchema, TemporalRepository<RoleTemplateDto>> emits the expected 5 DDL statements (2 sidecars + entity table + 2 indexes) with composite-FK + ON UPDATE CASCADE on both sidecars. 11 of 11 tests pass. RoleTemplateDto is registered as temporal via OATPP_AUTHKIT_REGISTER_TEMPORAL so TemporalRepository compiles cleanly. Atlas binary integration in CI is documented but not yet wired — owner deferred to a follow-up after the first concrete consumer migration. The shipped role_templates stack itself is fully consumable today; fewo- webapp's switch from local copies to oatpp-authkit-shipped headers is the natural next PR. Bumped 0.9.0 → 0.10.0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
114 lines
4.6 KiB
C++
114 lines
4.6 KiB
C++
// Tests for authkit#14 PR 1 — role_templates schema contribution composes
|
|
// correctly with the TemporalRepository decorator.
|
|
|
|
#include "oatpp-authkit/db/RoleTemplateDb.hpp"
|
|
#include "oatpp-authkit/dto/RoleTemplateDto.hpp"
|
|
#include "oatpp-authkit/repo/ConcreteRoleTemplateRepository.hpp"
|
|
#include "oatpp-authkit/repo/SchemaContract.hpp"
|
|
#include "oatpp-authkit/repo/TemporalRepository.hpp"
|
|
|
|
#include <cassert>
|
|
#include <cstdio>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#define REQUIRE(cond) do { \
|
|
if (!(cond)) { std::fprintf(stderr, "REQUIRE failed: %s @ %s:%d\n", \
|
|
#cond, __FILE__, __LINE__); std::abort(); } } while (0)
|
|
|
|
namespace {
|
|
|
|
bool contains(const std::string& haystack, const std::string& needle) {
|
|
return haystack.find(needle) != std::string::npos;
|
|
}
|
|
|
|
// SchemaBuilder<RoleTemplateSchema, TemporalRepository<RoleTemplateDto>>
|
|
// emits the three tables + their indexes. Verify the composition.
|
|
void test_role_templates_full_create() {
|
|
using namespace oatpp_authkit::repo;
|
|
using namespace oatpp_authkit::db;
|
|
using namespace oatpp_authkit::dto;
|
|
|
|
std::vector<std::string> sqls;
|
|
SqlExec exec = [&](const std::string& sql) { sqls.push_back(sql); };
|
|
|
|
SchemaBuilder<
|
|
RoleTemplateSchema,
|
|
TemporalRepository<RoleTemplateDto>>::create("role_templates", exec);
|
|
|
|
// Two sidecars (role_template_fields + user_role_assignments) +
|
|
// one entity table + one entity_id index + one composite UNIQUE index
|
|
// = 5
|
|
REQUIRE(sqls.size() == 5);
|
|
|
|
// Sidecar 1: role_template_fields with composite FK
|
|
REQUIRE(contains(sqls[0], "CREATE TABLE IF NOT EXISTS role_template_fields"));
|
|
REQUIRE(contains(sqls[0], "template_id TEXT NOT NULL"));
|
|
REQUIRE(contains(sqls[0], "template_valid_until TEXT NOT NULL DEFAULT '9999-12-31T23:59:59Z'"));
|
|
REQUIRE(contains(sqls[0],
|
|
"FOREIGN KEY (template_id, template_valid_until) REFERENCES "
|
|
"role_templates(entity_id, valid_until) ON UPDATE CASCADE"));
|
|
|
|
// Sidecar 2: user_role_assignments with composite FK
|
|
REQUIRE(contains(sqls[1], "CREATE TABLE IF NOT EXISTS user_role_assignments"));
|
|
REQUIRE(contains(sqls[1], "user_id TEXT NOT NULL"));
|
|
REQUIRE(contains(sqls[1],
|
|
"FOREIGN KEY (template_id, template_valid_until) REFERENCES "
|
|
"role_templates(entity_id, valid_until) ON UPDATE CASCADE"));
|
|
|
|
// Entity table: role_templates with all RoleTemplateSchema columns +
|
|
// valid_until from TemporalRepository.
|
|
REQUIRE(contains(sqls[2], "CREATE TABLE IF NOT EXISTS role_templates"));
|
|
REQUIRE(contains(sqls[2], "id TEXT PRIMARY KEY"));
|
|
REQUIRE(contains(sqls[2], "entity_id TEXT NOT NULL"));
|
|
REQUIRE(contains(sqls[2], "name TEXT NOT NULL"));
|
|
REQUIRE(contains(sqls[2], "is_system INTEGER NOT NULL DEFAULT 0"));
|
|
REQUIRE(contains(sqls[2], "valid_from TEXT NOT NULL DEFAULT (datetime('now'))"));
|
|
REQUIRE(contains(sqls[2], "valid_until TEXT NOT NULL DEFAULT '9999-12-31T23:59:59Z'"));
|
|
|
|
// Indexes: ix_role_templates_entity_id (RoleTemplateSchema)
|
|
// ux_role_templates_entity_valid_until (TemporalRepository)
|
|
REQUIRE(contains(sqls[3], "CREATE INDEX IF NOT EXISTS ix_role_templates_entity_id"));
|
|
REQUIRE(contains(sqls[3], "ON role_templates (entity_id)"));
|
|
REQUIRE(contains(sqls[4], "CREATE UNIQUE INDEX IF NOT EXISTS ux_role_templates_entity_valid_until"));
|
|
REQUIRE(contains(sqls[4], "ON role_templates (entity_id, valid_until)"));
|
|
}
|
|
|
|
// Verify that ConcreteRoleTemplateRepository contributes nothing to the
|
|
// schema — RoleTemplateSchema owns the table declarations, the concrete
|
|
// repo only adapts queries. Stacking the concrete repo into the builder
|
|
// must not duplicate columns.
|
|
void test_concrete_repo_contributes_no_schema() {
|
|
using namespace oatpp_authkit::repo;
|
|
using namespace oatpp_authkit::db;
|
|
using namespace oatpp_authkit::dto;
|
|
|
|
std::vector<std::string> sqls_with;
|
|
std::vector<std::string> sqls_without;
|
|
|
|
SchemaBuilder<
|
|
RoleTemplateSchema,
|
|
ConcreteRoleTemplateRepository,
|
|
TemporalRepository<RoleTemplateDto>>::create(
|
|
"role_templates",
|
|
[&](const std::string& s){ sqls_with.push_back(s); });
|
|
|
|
SchemaBuilder<
|
|
RoleTemplateSchema,
|
|
TemporalRepository<RoleTemplateDto>>::create(
|
|
"role_templates",
|
|
[&](const std::string& s){ sqls_without.push_back(s); });
|
|
|
|
// Including ConcreteRoleTemplateRepository in the pack changes nothing
|
|
// — empty kSchema contributes no DDL.
|
|
REQUIRE(sqls_with == sqls_without);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main() {
|
|
test_role_templates_full_create();
|
|
test_concrete_repo_contributes_no_schema();
|
|
std::printf("test_role_template_schema: OK\n");
|
|
return 0;
|
|
}
|