oatpp-authkit/test/test_user_permission_schema.cpp
Uwe Schuster 0bb8bef634 #14 PRs 2 & 3: relocate user_property_permissions + user_group_permissions
Lifts both per-property and per-property-set RBAC tables from fewo-webapp
into oatpp-authkit. Combined into one commit because they share a
DbClient and the cross-table effective-permission resolver — the resolver
itself stays in fewo since it joins property_set_members (a fewo-side
concept).

New files (all in oatpp-authkit):
- dto/UserPermissionDto.hpp — UserPropertyPermissionDto +
  UserGroupPermissionDto, both registered as temporal.
  EffectivePermissionDto stays in fewo (it's the result shape of fewo's
  property_set_members JOIN).
- db/UserPermissionDb.hpp — DbClient with CRUD for both tables. Each
  table also has a *Schema struct exposing kSchema for SchemaBuilder
  composition. Natural-key UNIQUE indexes carried explicitly:
  ux_..._user_property_until, ux_..._user_set_until.
- repo/ConcreteUserPermissionRepository.hpp — two concrete repos +
  makeUserPropertyPermissionRepository / makeUserGroupPermissionRepository
  factories that wrap each in TemporalRepository.
- test/test_user_permission_schema.cpp — verifies both schemas compose
  with TemporalRepository to produce the expected 5 DDL statements each
  (entity table + 3 schema indexes + 1 temporal composite index).

12 of 12 tests pass. Bumped 0.10.0 → 0.11.0.

Per-row natural-key UNIQUE prevents duplicate live grants for the same
(user_id, property_id) or (user_id, set_id) pair while still allowing
historical rows for the same key (their valid_until differs).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 12:39:52 +02:00

82 lines
3.1 KiB
C++

// Tests for authkit#14 PRs 2 & 3 — user_property_permissions and
// user_group_permissions schemas compose correctly with TemporalRepository.
#include "oatpp-authkit/db/UserPermissionDb.hpp"
#include "oatpp-authkit/dto/UserPermissionDto.hpp"
#include "oatpp-authkit/repo/ConcreteUserPermissionRepository.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;
}
void test_user_property_permissions_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<
UserPropertyPermissionSchema,
TemporalRepository<UserPropertyPermissionDto>>::create(
"user_property_permissions", exec);
// 1 entity table + 3 schema-side indexes + 1 temporal index = 5
REQUIRE(sqls.size() == 5);
REQUIRE(contains(sqls[0], "CREATE TABLE IF NOT EXISTS user_property_permissions"));
REQUIRE(contains(sqls[0], "user_id TEXT NOT NULL"));
REQUIRE(contains(sqls[0], "property_id TEXT NOT NULL"));
REQUIRE(contains(sqls[0], "permission TEXT NOT NULL DEFAULT 'readonly'"));
REQUIRE(contains(sqls[0], "valid_until TEXT NOT NULL DEFAULT '9999-12-31T23:59:59Z'"));
// Indexes: 3 from UserPropertyPermissionSchema in order, then 1 from TemporalRepository.
REQUIRE(contains(sqls[1], "ix_user_property_permissions_entity_id"));
REQUIRE(contains(sqls[2], "ix_user_property_permissions_user_id"));
REQUIRE(contains(sqls[3], "ux_user_property_permissions_user_property_until"));
REQUIRE(contains(sqls[3], "(user_id, property_id, valid_until)"));
REQUIRE(contains(sqls[4], "ux_user_property_permissions_entity_valid_until"));
}
void test_user_group_permissions_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<
UserGroupPermissionSchema,
TemporalRepository<UserGroupPermissionDto>>::create(
"user_group_permissions", exec);
REQUIRE(sqls.size() == 5);
REQUIRE(contains(sqls[0], "CREATE TABLE IF NOT EXISTS user_group_permissions"));
REQUIRE(contains(sqls[0], "set_id TEXT NOT NULL"));
REQUIRE(contains(sqls[3], "ux_user_group_permissions_user_set_until"));
REQUIRE(contains(sqls[3], "(user_id, set_id, valid_until)"));
}
} // namespace
int main() {
test_user_property_permissions_create();
test_user_group_permissions_create();
std::printf("test_user_permission_schema: OK\n");
return 0;
}