Commit 5f245939 authored by jan.koester's avatar jan.koester
Browse files

test

parent a1a2c692
Loading
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
authdb (20260417+1) unstable; urgency=medium

  * Fix missing Username and GPO information in SessionData due to
    incomplete copy constructors
  * Add unit tests for Cluster Operations (session push, fetch, rebalance)

 -- Jan Koester <jan.koester@tuxist.de>  Fri, 17 Apr 2026 12:00:00 +0200

authdb (20260416+4) unstable; urgency=medium

  * Increase initial cluster retrieval timeout to 30s to fix startup
+28 −0
Original line number Diff line number Diff line
@@ -57,6 +57,20 @@ authdb::SessionData::SessionData(const SessionData& src) : SessionData() {
         _sid=src._sid;
         _uid=src._uid;
         _did=src._did;
         _username=src._username;
         _gpoIndex=src._gpoIndex;
         
         SessionData::GPOResult *cur=src._GPOResult.next;
         SessionData::GPOResult *dest=&_GPOResult;
         dest->GPOId=src._GPOResult.GPOId;
         dest->GPORes=src._GPOResult.GPORes;
         while(cur){
             dest->next=new SessionData::GPOResult();
             dest=dest->next;
             dest->GPOId=cur->GPOId;
             dest->GPORes=cur->GPORes;
             cur=cur->next;
         }
}

authdb::SessionData::SessionData(const SessionData *src) : SessionData() {
@@ -65,6 +79,20 @@ authdb::SessionData::SessionData(const SessionData *src) : SessionData() {
         _sid=src->_sid;
         _uid=src->_uid;
         _did=src->_did;
         _username=src->_username;
         _gpoIndex=src->_gpoIndex;

         SessionData::GPOResult *cur=src->_GPOResult.next;
         SessionData::GPOResult *dest=&_GPOResult;
         dest->GPOId=src->_GPOResult.GPOId;
         dest->GPORes=src->_GPOResult.GPORes;
         while(cur){
             dest->next=new SessionData::GPOResult();
             dest=dest->next;
             dest->GPOId=cur->GPOId;
             dest->GPORes=cur->GPORes;
             cur=cur->next;
         }
}

authdb::SessionData::SessionData(uuid::uuid sessionid, uuid::uuid userid, uuid::uuid domainid,std::vector<uuid::uuid> &members) : _members(members){
+9 −0
Original line number Diff line number Diff line
@@ -87,3 +87,12 @@ target_link_libraries(test_cluster_performance PRIVATE
    GTest::gtest_main
)
gtest_discover_tests(test_cluster_performance)

# ── Cluster operations (scrub, replicate, etc) ──
add_executable(test_cluster_ops test_cluster_ops.cpp)
target_include_directories(test_cluster_ops PRIVATE ${CMAKE_SOURCE_DIR}/src)
target_link_libraries(test_cluster_ops PRIVATE
    authobj
    GTest::gtest_main
)
gtest_discover_tests(test_cluster_ops)
+222 −0
Original line number Diff line number Diff line
#include <gtest/gtest.h>
#include <chrono>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <memory>
#include <numeric>
#include <string>
#include <thread>
#include <vector>

#ifdef _WIN32
#include <process.h>
#include <io.h>
#include <windows.h>
#include <psapi.h>
#define test_getpid  _getpid
#define test_unlink  _unlink
#else
#include <unistd.h>
#define test_getpid  getpid
#define test_unlink  unlink
#endif

#include <uuidp.h>
#include <netplus/utils/certgen.h>

#include "backend.h"
#include "authdb.h"
#include "user.h"
#include "group.h"
#include "gpo.h"
#include "session.h"
#include "types.h"
#include "exception.h"
#include "cluster.h"
#include "client.h"
#include "index.h"

namespace authdb {
    extern std::mutex AuthClientLock;
    extern Cluster *g_Cluster;
}

using namespace authdb;

static std::string tempDir() {
#ifdef _WIN32
    const char *tmp = std::getenv("TEMP");
    if (!tmp) tmp = std::getenv("TMP");
    return tmp ? std::string(tmp) + "\\" : "C:\\Temp\\";
#else
    return "/tmp/";
#endif
}

static std::string g_certFile;
static std::string g_keyFile;

static bool generateTestCerts() {
    std::string prefix = tempDir() + "authdb_cluster_ops_test_" + std::to_string(test_getpid());
    g_certFile = prefix + ".crt";
    g_keyFile  = prefix + ".key.der";

    netplus::CertGenConfig cfg;
    cfg.commonName = "authdb-test";
    cfg.rsaBits    = 2048;
    cfg.validDays  = 1;

    netplus::CertKeyPair pair;
    if (!netplus::generateSelfSignedCert(cfg, pair)) {
        return false;
    }

    if (!netplus::writePemFile(g_certFile, pair.certPem)) return false;
    if (!netplus::writeDerFile(g_keyFile, pair.keyDer)) return false;

    return true;
}

static void cleanupTestCerts() {
    if (!g_certFile.empty()) test_unlink(g_certFile.c_str());
    if (!g_keyFile.empty()) test_unlink(g_keyFile.c_str());
}

static constexpr int NUM_NODES = 3;

class ClusterOpsTest : public ::testing::Test {
protected:
    static Cluster *clusters[NUM_NODES];
    static std::string fileDbPath;
    static int basePorts[NUM_NODES];
    static bool clusterReady;

    static void SetUpTestSuite() {
        if (!generateTestCerts()) {
            GTEST_SKIP() << "Could not generate TLS certificates";
            return;
        }

        int basePort = 24433 + (test_getpid() % 10000);
        for (int i = 0; i < NUM_NODES; ++i)
            basePorts[i] = basePort + i;

        fileDbPath = tempDir() + "authdb_cluster_ops_" + std::to_string(test_getpid()) + ".db";

        std::vector<ClusterNode> allPeers;
        for (int i = 0; i < NUM_NODES; ++i) {
            ClusterNode node;
            node.address = "127.0.0.1";
            node.port = basePorts[i];
            allPeers.push_back(node);
        }

        for (int i = 0; i < NUM_NODES; ++i) {
            clusters[i] = new Cluster();

            ClusterConfig cfg;
            cfg.bind_address = "127.0.0.1";
            cfg.port = basePorts[i];
            cfg.cert_file = g_certFile;
            cfg.key_file = g_keyFile;
            cfg.client_name = "testops";
            cfg.client_key = "testkeyops";
            cfg.store_path = "";  // memory-only for fast testing
            cfg.data_blocks = 2;
            cfg.parity_blocks = 1;
            cfg.peers = allPeers;

            clusters[i]->init(cfg);
            clusters[i]->start();
        }

        authdb::g_Cluster = clusters[0];

        size_t online = clusters[0]->waitForPeers(10);
        clusterReady = (online >= 2);
    }

    static void TearDownTestSuite() {
        authdb::g_Cluster = nullptr;
        for (int i = 0; i < NUM_NODES; ++i) {
            if (clusters[i]) {
                clusters[i]->stop();
                delete clusters[i];
                clusters[i] = nullptr;
            }
        }
        test_unlink(fileDbPath.c_str());
        cleanupTestCerts();
    }

    void SetUp() override {
        if (!clusterReady)
            GTEST_SKIP() << "Cluster not ready";
    }
};

Cluster *ClusterOpsTest::clusters[NUM_NODES] = {};
std::string ClusterOpsTest::fileDbPath;
int ClusterOpsTest::basePorts[NUM_NODES] = {};
bool ClusterOpsTest::clusterReady = false;

// ── Test Session Push & Fetch ──
TEST_F(ClusterOpsTest, SessionPushAndFetch) {
    uuid::uuid sid, uid, did;
    sid.generate();
    uid.generate();
    did.generate();
    std::vector<uuid::uuid> members;

    SessionData sd(sid, uid, did, members);

    // Push via node 0
    clusters[0]->pushSession(sd);

    // Give it a moment to replicate
    std::this_thread::sleep_for(std::chrono::milliseconds(200));

    // Try fetching from node 0
    uuid::uuid f_sid, f_uid, f_did;
    std::vector<uuid::uuid> f_members;
    std::string f_username;
    std::vector<std::pair<uuid::uuid, bool>> f_gpos;
    
    bool ok = clusters[0]->fetchSessionBySid(sid, f_sid, f_uid, f_did, f_members, f_username, f_gpos);
    EXPECT_TRUE(ok);
    EXPECT_EQ(f_sid, sid);
    EXPECT_EQ(f_uid, uid);
    EXPECT_EQ(f_did, did);

    // Wait and scrub 
    auto res = clusters[0]->scrub();
    EXPECT_GE(res.groups_checked, 0u);
}

// ── Test Data Replication & Scrub ──
TEST_F(ClusterOpsTest, ReplicateOpAndScrub) {
    uuid::uuid recId;
    recId.generate();

    // Replicate an op on node 0
    clusters[0]->replicateOp(authdb::OpType::Create, "testdomain", recId, 1 /* UserData */, nullptr);

    // Provide some time for push
    std::this_thread::sleep_for(std::chrono::milliseconds(200));

    // Run scrub on node 0
    auto res = clusters[0]->scrub();
    
    // We expect some repaired or checked groups, at least 0 if it was skipped
    EXPECT_GE(res.groups_checked, 0u);
}

// ── Test Rebalance ──
TEST_F(ClusterOpsTest, RebalanceOp) {
    auto &client = clusters[0]->getClient();
    auto rb = client->rebalance();
    EXPECT_GE(rb.rebalanced, 0u);
}