aissia/PLAN_TESTS_INTEGRATION.md

27 KiB

Plan d'Implementation - Tests d'Integration AISSIA

Vue d'Ensemble

Ce document decrit le plan complet pour implementer 110 tests d'integration (TI) :

  • 10 TI par module (6 modules = 60 TI)
  • 50 TI pour le systeme MCP

Architecture des Tests

tests/
├── CMakeLists.txt                    # Configuration tests
├── main.cpp                          # Entry point Catch2
├── mocks/
│   ├── MockIO.hpp                    # Mock IIO pub/sub
│   ├── MockDataNode.hpp              # Mock IDataNode
│   ├── MockTaskScheduler.hpp         # Mock ITaskScheduler
│   ├── MockTransport.hpp             # Mock IMCPTransport
│   └── MockLLMProvider.hpp           # Mock ILLMProvider
├── utils/
│   ├── TestHelpers.hpp               # Utilitaires communs
│   ├── MessageCapture.hpp            # Capture messages IIO
│   └── TimeSimulator.hpp             # Simulation temps (gameTime)
├── modules/
│   ├── SchedulerModuleTests.cpp      # 10 TI
│   ├── NotificationModuleTests.cpp   # 10 TI
│   ├── MonitoringModuleTests.cpp     # 10 TI
│   ├── AIModuleTests.cpp             # 10 TI
│   ├── VoiceModuleTests.cpp          # 10 TI
│   └── StorageModuleTests.cpp        # 10 TI
└── mcp/
    ├── MCPTypesTests.cpp             # 15 TI
    ├── StdioTransportTests.cpp       # 20 TI
    └── MCPClientTests.cpp            # 15 TI

Phase 1: Infrastructure (Priorite Haute)

1.1 Mocks Essentiels

MockIO.hpp

class MockIO : public grove::IIO {
public:
    // Capture des messages publies
    std::vector<std::pair<std::string, json>> publishedMessages;

    // Queue de messages a recevoir
    std::queue<grove::Message> incomingMessages;

    void publish(const std::string& topic, const grove::IDataNode& data) override;
    bool hasMessages() const override;
    grove::Message popMessage() override;

    // Helpers de test
    void injectMessage(const std::string& topic, const json& data);
    bool wasPublished(const std::string& topic) const;
    json getLastPublished(const std::string& topic) const;
    void clear();
};

MockTransport.hpp

class MockTransport : public aissia::mcp::IMCPTransport {
public:
    bool m_running = false;
    std::vector<JsonRpcRequest> sentRequests;
    std::queue<JsonRpcResponse> preparedResponses;

    bool start() override;
    void stop() override;
    bool isRunning() const override;
    JsonRpcResponse sendRequest(const JsonRpcRequest& request, int timeoutMs) override;

    // Test helpers
    void prepareResponse(const JsonRpcResponse& response);
    void setStartFailure(bool fail);
};

1.2 Utilitaires

TimeSimulator.hpp

class TimeSimulator {
public:
    float m_gameTime = 0.0f;

    json createInput(float deltaTime = 0.1f);
    void advance(float seconds);
    void setTime(float time);
};

MessageCapture.hpp

class MessageCapture {
public:
    void captureFrom(MockIO& io);
    bool waitForMessage(const std::string& topic, int timeoutMs = 1000);
    json getMessage(const std::string& topic);
    int countMessages(const std::string& topic);
};

Phase 2: Tests des Modules (60 TI)

2.1 SchedulerModule (10 TI)

# Test Description Topics Verifies
1 TI_SCHEDULER_001_StartTask Demarrer une tache publie scheduler:task_started scheduler:task_started
2 TI_SCHEDULER_002_CompleteTask Completer une tache publie scheduler:task_completed avec duree scheduler:task_completed
3 TI_SCHEDULER_003_HyperfocusDetection Session > 120min declenche scheduler:hyperfocus_alert scheduler:hyperfocus_alert
4 TI_SCHEDULER_004_HyperfocusAlertOnce Alerte hyperfocus envoyee une seule fois par session single publish
5 TI_SCHEDULER_005_BreakReminder Rappel de pause toutes les 45min scheduler:break_reminder
6 TI_SCHEDULER_006_IdlePausesSession Reception monitoring:idle_detected pause le tracking state
7 TI_SCHEDULER_007_ActivityResumesSession Reception monitoring:activity_resumed reprend le tracking state
8 TI_SCHEDULER_008_ToolQueryGetCurrentTask Query tool get_current_task retourne tache courante scheduler:response
9 TI_SCHEDULER_009_ToolCommandStartBreak Command tool start_break publie scheduler:break_started scheduler:break_started
10 TI_SCHEDULER_010_StateSerialization getState() et setState() preservent l'etat complet state roundtrip

Implementation:

TEST_CASE("TI_SCHEDULER_001_StartTask", "[scheduler][integration]") {
    MockIO io;
    SchedulerModule module;
    TimeSimulator time;

    // Configure
    json config = {{"hyperfocusThresholdMinutes", 120}};
    module.setConfiguration(JsonDataNode(config), &io, nullptr);

    // Add task
    io.injectMessage("user:task_switch", {{"taskId", "task-1"}});

    // Process
    module.process(JsonDataNode(time.createInput()));

    // Verify
    REQUIRE(io.wasPublished("scheduler:task_started"));
    auto msg = io.getLastPublished("scheduler:task_started");
    REQUIRE(msg["taskId"] == "task-1");
}

2.2 NotificationModule (10 TI)

# Test Description Topics Verifies
1 TI_NOTIF_001_QueueNotification notify() ajoute a la queue state queue size
2 TI_NOTIF_002_ProcessQueue process() traite max 3 notifications/frame queue drain
3 TI_NOTIF_003_PriorityOrdering URGENT traite avant NORMAL order
4 TI_NOTIF_004_SilentModeBlocksNonUrgent Mode silencieux bloque LOW/NORMAL/HIGH filtering
5 TI_NOTIF_005_SilentModeAllowsUrgent Mode silencieux laisse passer URGENT filtering
6 TI_NOTIF_006_MaxQueueSize Queue limitee a maxQueueSize (50) overflow
7 TI_NOTIF_007_LanguageConfig Langue configuree via setConfiguration config
8 TI_NOTIF_008_NotificationCountTracking Compteurs notificationCount et urgentCount state
9 TI_NOTIF_009_StateSerialization getState()/setState() preservent queue et compteurs state roundtrip
10 TI_NOTIF_010_MultipleFrameProcessing Queue > 3 elements necessite plusieurs frames multi-frame

Implementation:

TEST_CASE("TI_NOTIF_004_SilentModeBlocksNonUrgent", "[notification][integration]") {
    MockIO io;
    NotificationModule module;
    TimeSimulator time;

    json config = {{"silentMode", true}};
    module.setConfiguration(JsonDataNode(config), &io, nullptr);

    module.notify("Test", "Normal message", NotificationModule::Priority::NORMAL);
    module.notify("Test", "High message", NotificationModule::Priority::HIGH);

    // State should show 0 pending (blocked)
    auto state = module.getState();
    REQUIRE(state->getInt("pendingCount") == 0);
}

2.3 MonitoringModule (10 TI)

# Test Description Topics Verifies
1 TI_MONITOR_001_AppChanged Reception platform:window_changed publie monitoring:app_changed monitoring:app_changed
2 TI_MONITOR_002_ProductiveAppClassification Apps dans productiveApps classees "productive" classification
3 TI_MONITOR_003_DistractingAppClassification Apps dans distractingApps classees "distracting" classification
4 TI_MONITOR_004_NeutralAppClassification Apps inconnues classees "neutral" classification
5 TI_MONITOR_005_DurationTracking m_appDurations accumule temps par app duration map
6 TI_MONITOR_006_IdleDetectedPausesTracking platform:idle_detected pause accumulation state
7 TI_MONITOR_007_ActivityResumedResumesTracking platform:activity_resumed reprend accumulation state
8 TI_MONITOR_008_ProductivityStats m_totalProductiveSeconds et m_totalDistractingSeconds corrects stats
9 TI_MONITOR_009_ToolQueryGetCurrentApp Query get_current_app retourne app courante monitoring:response
10 TI_MONITOR_010_StateSerialization getState()/setState() preservent stats et durations state roundtrip

Implementation:

TEST_CASE("TI_MONITOR_002_ProductiveAppClassification", "[monitoring][integration]") {
    MockIO io;
    MonitoringModule module;
    TimeSimulator time;

    json config = {
        {"productive_apps", {"Code", "CLion", "Visual Studio"}}
    };
    module.setConfiguration(JsonDataNode(config), &io, nullptr);

    io.injectMessage("platform:window_changed", {
        {"oldApp", ""},
        {"newApp", "Code"},
        {"duration", 0}
    });

    module.process(JsonDataNode(time.createInput()));

    REQUIRE(io.wasPublished("monitoring:app_changed"));
    auto msg = io.getLastPublished("monitoring:app_changed");
    REQUIRE(msg["classification"] == "productive");
}

2.4 AIModule (10 TI)

# Test Description Topics Verifies
1 TI_AI_001_QuerySendsLLMRequest Reception ai:query publie llm:request llm:request
2 TI_AI_002_VoiceTranscriptionTriggersQuery Reception voice:transcription envoie query llm:request
3 TI_AI_003_LLMResponseHandled Reception llm:response met m_awaitingResponse = false state
4 TI_AI_004_LLMErrorHandled Reception llm:error met m_awaitingResponse = false state
5 TI_AI_005_HyperfocusAlertGeneratesSuggestion scheduler:hyperfocus_alert publie ai:suggestion ai:suggestion
6 TI_AI_006_BreakReminderGeneratesSuggestion scheduler:break_reminder publie ai:suggestion ai:suggestion
7 TI_AI_007_SystemPromptInRequest llm:request contient systemPrompt de config request content
8 TI_AI_008_ConversationIdTracking Requetes utilisent m_currentConversationId conversation
9 TI_AI_009_TokenCountingAccumulates m_totalTokens s'accumule apres chaque reponse state
10 TI_AI_010_StateSerialization getState()/setState() preservent compteurs et conversation state roundtrip

Implementation:

TEST_CASE("TI_AI_005_HyperfocusAlertGeneratesSuggestion", "[ai][integration]") {
    MockIO io;
    AIModule module;
    TimeSimulator time;

    json config = {{"system_prompt", "Tu es un assistant"}};
    module.setConfiguration(JsonDataNode(config), &io, nullptr);

    io.injectMessage("scheduler:hyperfocus_alert", {
        {"sessionMinutes", 130},
        {"task", "coding"}
    });

    module.process(JsonDataNode(time.createInput()));

    REQUIRE(io.wasPublished("ai:suggestion"));
    auto msg = io.getLastPublished("ai:suggestion");
    REQUIRE(msg.contains("message"));
}

2.5 VoiceModule (10 TI)

# Test Description Topics Verifies
1 TI_VOICE_001_AIResponseTriggersSpeak Reception ai:response publie voice:speak voice:speak
2 TI_VOICE_002_SuggestionPrioritySpeak Reception ai:suggestion publie voice:speak avec priorite voice:speak priority
3 TI_VOICE_003_SpeakingStartedUpdatesState voice:speaking_started met m_isSpeaking = true state
4 TI_VOICE_004_SpeakingEndedUpdatesState voice:speaking_ended met m_isSpeaking = false state
5 TI_VOICE_005_IsIdleReflectsSpeaking isIdle() retourne !m_isSpeaking interface
6 TI_VOICE_006_TranscriptionForwarded voice:transcription non traite par VoiceModule (forward only) no re-publish
7 TI_VOICE_007_TotalSpokenIncremented m_totalSpoken incremente apres chaque speaking_ended counter
8 TI_VOICE_008_TTSDisabledConfig Config ttsEnabled: false empeche voice:speak config
9 TI_VOICE_009_ToolCommandSpeak Command tool speak publie voice:speak tool
10 TI_VOICE_010_StateSerialization getState()/setState() preservent compteurs state roundtrip

Implementation:

TEST_CASE("TI_VOICE_002_SuggestionPrioritySpeak", "[voice][integration]") {
    MockIO io;
    VoiceModule module;
    TimeSimulator time;

    json config = {{"ttsEnabled", true}};
    module.setConfiguration(JsonDataNode(config), &io, nullptr);

    io.injectMessage("ai:suggestion", {
        {"message", "Tu devrais faire une pause"},
        {"duration", 5}
    });

    module.process(JsonDataNode(time.createInput()));

    REQUIRE(io.wasPublished("voice:speak"));
    auto msg = io.getLastPublished("voice:speak");
    REQUIRE(msg["priority"] == true);
}

2.6 StorageModule (10 TI)

# Test Description Topics Verifies
1 TI_STORAGE_001_TaskCompletedSavesSession scheduler:task_completed publie storage:save_session storage:save_session
2 TI_STORAGE_002_AppChangedSavesUsage monitoring:app_changed publie storage:save_app_usage storage:save_app_usage
3 TI_STORAGE_003_SessionSavedUpdatesLastId storage:session_saved met a jour m_lastSessionId state
4 TI_STORAGE_004_StorageErrorHandled storage:error log l'erreur sans crash error handling
5 TI_STORAGE_005_PendingSavesTracking m_pendingSaves incremente/decremente correctement counter
6 TI_STORAGE_006_TotalSavedTracking m_totalSaved s'accumule counter
7 TI_STORAGE_007_ToolQueryNotes Query query_notes retourne notes filtrees storage:response
8 TI_STORAGE_008_ToolCommandSaveNote Command save_note ajoute note a m_notes state
9 TI_STORAGE_009_NoteTagsFiltering Query notes avec tags filtre correctement filtering
10 TI_STORAGE_010_StateSerialization getState()/setState() preservent notes et compteurs state roundtrip

Phase 3: Tests MCP (50 TI)

3.1 MCPTypes (15 TI)

# Test Description
1 TI_TYPES_001_MCPToolToJson MCPTool::toJson() serialise correctement
2 TI_TYPES_002_MCPToolFromJson MCPTool::fromJson() deserialise correctement
3 TI_TYPES_003_MCPToolFromJsonMissingFields fromJson() avec champs manquants utilise defauts
4 TI_TYPES_004_MCPResourceFromJson MCPResource::fromJson() deserialise correctement
5 TI_TYPES_005_MCPToolResultToJson MCPToolResult::toJson() serialise content et isError
6 TI_TYPES_006_MCPCapabilitiesFromJson Detection correcte des capabilities
7 TI_TYPES_007_MCPCapabilitiesEmpty Capabilities vides si pas de champs
8 TI_TYPES_008_MCPServerInfoFromJson MCPServerInfo::fromJson() parse name/version/caps
9 TI_TYPES_009_JsonRpcRequestToJson Serialisation avec/sans params
10 TI_TYPES_010_JsonRpcResponseFromJson Parse result ou error
11 TI_TYPES_011_JsonRpcResponseIsError isError() detecte presence de error
12 TI_TYPES_012_MCPServerConfigFromJson Parse command, args, env, enabled
13 TI_TYPES_013_MCPServerConfigEnvExpansion Variables env ${VAR} expandees
14 TI_TYPES_014_MCPServerConfigDisabled enabled: false honore
15 TI_TYPES_015_JsonRpcRequestIdIncrement IDs uniques et croissants

Implementation:

TEST_CASE("TI_TYPES_001_MCPToolToJson", "[mcp][types]") {
    MCPTool tool;
    tool.name = "read_file";
    tool.description = "Read a file";
    tool.inputSchema = {{"type", "object"}, {"properties", {{"path", {{"type", "string"}}}}}};

    json j = tool.toJson();

    REQUIRE(j["name"] == "read_file");
    REQUIRE(j["description"] == "Read a file");
    REQUIRE(j["inputSchema"]["type"] == "object");
}

TEST_CASE("TI_TYPES_011_JsonRpcResponseIsError", "[mcp][types]") {
    json errorJson = {
        {"jsonrpc", "2.0"},
        {"id", 1},
        {"error", {{"code", -32600}, {"message", "Invalid Request"}}}
    };

    auto response = JsonRpcResponse::fromJson(errorJson);

    REQUIRE(response.isError() == true);
    REQUIRE(response.error.value()["code"] == -32600);
}

3.2 StdioTransport (20 TI)

# Test Description
1 TI_TRANSPORT_001_StartSpawnsProcess start() lance le processus enfant
2 TI_TRANSPORT_002_StartFailsInvalidCommand start() retourne false si commande invalide
3 TI_TRANSPORT_003_StopKillsProcess stop() termine le processus
4 TI_TRANSPORT_004_IsRunningReflectsState isRunning() reflete l'etat reel
5 TI_TRANSPORT_005_SendRequestWritesToStdin Request serialisee vers stdin
6 TI_TRANSPORT_006_SendRequestReadsResponse Response lue depuis stdout
7 TI_TRANSPORT_007_SendRequestTimeout Timeout si pas de reponse
8 TI_TRANSPORT_008_SendRequestIdMatching Response matchee par ID
9 TI_TRANSPORT_009_ConcurrentRequests Multiple requests simultanées OK
10 TI_TRANSPORT_010_SendNotificationNoResponse Notification n'attend pas de reponse
11 TI_TRANSPORT_011_ReaderThreadStartsOnStart Thread reader demarre avec start()
12 TI_TRANSPORT_012_ReaderThreadStopsOnStop Thread reader s'arrete avec stop()
13 TI_TRANSPORT_013_JsonParseErrorHandled JSON invalide n'crash pas
14 TI_TRANSPORT_014_ProcessCrashDetected Crash process detecte rapidement
15 TI_TRANSPORT_015_LargeMessageHandling Messages > 64KB transmis
16 TI_TRANSPORT_016_MultilineJsonHandling JSON multiline traite correctement
17 TI_TRANSPORT_017_EnvVariablesPassedToProcess Variables env transmises au processus
18 TI_TRANSPORT_018_ArgsPassedToProcess Arguments CLI transmis
19 TI_TRANSPORT_019_DestructorCleansUp Destructeur nettoie ressources
20 TI_TRANSPORT_020_RestartAfterStop start() apres stop() fonctionne

Implementation (avec mock process):

// Pour les tests, on peut creer un mini-serveur echo en Python ou utiliser un mock

TEST_CASE("TI_TRANSPORT_007_SendRequestTimeout", "[mcp][transport]") {
    MCPServerConfig config;
    config.command = "cat";  // cat ne repond jamais au JSON-RPC

    StdioTransport transport(config);
    transport.start();

    JsonRpcRequest request;
    request.id = 1;
    request.method = "test";

    auto response = transport.sendRequest(request, 100);  // 100ms timeout

    REQUIRE(response.isError() == true);
    transport.stop();
}

// Test avec echo server (Python script)
TEST_CASE("TI_TRANSPORT_006_SendRequestReadsResponse", "[mcp][transport]") {
    MCPServerConfig config;
    config.command = "python";
    config.args = {"tests/fixtures/echo_server.py"};

    StdioTransport transport(config);
    REQUIRE(transport.start() == true);

    JsonRpcRequest request;
    request.id = 42;
    request.method = "echo";
    request.params = {{"message", "hello"}};

    auto response = transport.sendRequest(request, 5000);

    REQUIRE(response.isError() == false);
    REQUIRE(response.id == 42);
    transport.stop();
}

3.3 MCPClient (15 TI)

# Test Description
1 TI_CLIENT_001_LoadConfigValid loadConfig() parse mcp.json valide
2 TI_CLIENT_002_LoadConfigInvalid loadConfig() gere fichier invalide
3 TI_CLIENT_003_LoadConfigMissingFile loadConfig() retourne false si fichier absent
4 TI_CLIENT_004_ConnectAllStartsServers connectAll() demarre tous les serveurs enabled
5 TI_CLIENT_005_ConnectAllSkipsDisabled connectAll() skip les serveurs enabled: false
6 TI_CLIENT_006_ConnectSingleServer connect(name) demarre un seul serveur
7 TI_CLIENT_007_DisconnectSingleServer disconnect(name) arrete un serveur
8 TI_CLIENT_008_DisconnectAllCleansUp disconnectAll() arrete tous les serveurs
9 TI_CLIENT_009_ListAllToolsAggregates listAllTools() combine tools de tous serveurs
10 TI_CLIENT_010_ToolNamePrefixed Tools prefixes par nom serveur (server:tool)
11 TI_CLIENT_011_CallToolRoutesToServer callTool() route vers bon serveur
12 TI_CLIENT_012_CallToolInvalidName callTool() avec nom invalide retourne erreur
13 TI_CLIENT_013_CallToolDisconnectedServer callTool() sur serveur deconnecte retourne erreur
14 TI_CLIENT_014_ToolCountAccurate toolCount() reflete nombre total de tools
15 TI_CLIENT_015_IsConnectedAccurate isConnected(name) reflete etat reel

Implementation:

TEST_CASE("TI_CLIENT_010_ToolNamePrefixed", "[mcp][client]") {
    MCPClient client;

    // Utiliser mock transport
    client.loadConfig("tests/fixtures/mock_mcp.json");
    // Le mock simule un serveur avec tool "read_file"

    client.connectAll();
    auto tools = client.listAllTools();

    bool hasPrefix = false;
    for (const auto& tool : tools) {
        if (tool.name.find(":") != std::string::npos) {
            hasPrefix = true;
            break;
        }
    }

    REQUIRE(hasPrefix == true);
    client.disconnectAll();
}

TEST_CASE("TI_CLIENT_012_CallToolInvalidName", "[mcp][client]") {
    MCPClient client;
    client.loadConfig("tests/fixtures/mock_mcp.json");
    client.connectAll();

    auto result = client.callTool("nonexistent:tool", {});

    REQUIRE(result.isError == true);
    client.disconnectAll();
}

Phase 4: Fixtures et Scripts de Test

4.1 Echo Server MCP (Python)

tests/fixtures/echo_server.py

#!/usr/bin/env python3
import json
import sys

def main():
    while True:
        line = sys.stdin.readline()
        if not line:
            break
        try:
            request = json.loads(line)
            response = {
                "jsonrpc": "2.0",
                "id": request.get("id"),
                "result": request.get("params", {})
            }
            sys.stdout.write(json.dumps(response) + "\n")
            sys.stdout.flush()
        except:
            pass

if __name__ == "__main__":
    main()

4.2 Mock MCP Server (pour tests d'integration)

tests/fixtures/mock_mcp_server.py

#!/usr/bin/env python3
import json
import sys

TOOLS = [
    {"name": "test_tool", "description": "A test tool", "inputSchema": {"type": "object"}}
]

def handle_request(request):
    method = request.get("method")

    if method == "initialize":
        return {
            "protocolVersion": "2024-11-05",
            "capabilities": {"tools": {}},
            "serverInfo": {"name": "MockServer", "version": "1.0"}
        }
    elif method == "tools/list":
        return {"tools": TOOLS}
    elif method == "tools/call":
        return {"content": [{"type": "text", "text": "Tool executed"}]}

    return {"error": {"code": -32601, "message": "Method not found"}}

def main():
    while True:
        line = sys.stdin.readline()
        if not line:
            break
        try:
            request = json.loads(line)
            result = handle_request(request)
            response = {"jsonrpc": "2.0", "id": request.get("id")}
            if "error" in result:
                response["error"] = result["error"]
            else:
                response["result"] = result
            sys.stdout.write(json.dumps(response) + "\n")
            sys.stdout.flush()
        except Exception as e:
            pass

if __name__ == "__main__":
    main()

4.3 Config Mock MCP

tests/fixtures/mock_mcp.json

{
    "mock_server": {
        "command": "python",
        "args": ["tests/fixtures/mock_mcp_server.py"],
        "enabled": true
    },
    "disabled_server": {
        "command": "nonexistent",
        "enabled": false
    }
}

Phase 5: CMakeLists.txt Tests

# ============================================================================
# Tests d'Integration
# ============================================================================

# Fetch Catch2 if not already available
FetchContent_Declare(
    Catch2
    GIT_REPOSITORY https://github.com/catchorg/Catch2.git
    GIT_TAG v3.4.0
)
FetchContent_MakeAvailable(Catch2)

# Test executable
add_executable(aissia_tests
    tests/main.cpp

    # Mocks
    tests/mocks/MockIO.cpp

    # Module tests
    tests/modules/SchedulerModuleTests.cpp
    tests/modules/NotificationModuleTests.cpp
    tests/modules/MonitoringModuleTests.cpp
    tests/modules/AIModuleTests.cpp
    tests/modules/VoiceModuleTests.cpp
    tests/modules/StorageModuleTests.cpp

    # MCP tests
    tests/mcp/MCPTypesTests.cpp
    tests/mcp/StdioTransportTests.cpp
    tests/mcp/MCPClientTests.cpp
)

target_link_libraries(aissia_tests PRIVATE
    Catch2::Catch2WithMain
    GroveEngine::impl
    AissiaTools
    spdlog::spdlog
)

target_include_directories(aissia_tests PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/src
    ${CMAKE_CURRENT_SOURCE_DIR}/tests
)

# Copy test fixtures
file(COPY tests/fixtures/ DESTINATION ${CMAKE_BINARY_DIR}/tests/fixtures)

# CTest integration
include(CTest)
include(Catch)
catch_discover_tests(aissia_tests)

# Custom target for running tests
add_custom_target(test_all
    COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
    DEPENDS aissia_tests
    COMMENT "Running all integration tests"
)

Ordre d'Implementation Recommande

Sprint 1: Infrastructure (2-3 jours)

  1. Creer structure tests/
  2. Implementer MockIO.hpp
  3. Implementer MockTransport.hpp
  4. Implementer TimeSimulator.hpp
  5. Setup CMakeLists.txt tests
  6. Creer fixtures Python

Sprint 2: Tests MCPTypes (1 jour)

  1. TI_TYPES_001 a TI_TYPES_015
  2. Valider serialisation/deserialisation

Sprint 3: Tests StdioTransport (2 jours)

  1. TI_TRANSPORT_001 a TI_TRANSPORT_010 (basic)
  2. TI_TRANSPORT_011 a TI_TRANSPORT_020 (advanced)

Sprint 4: Tests MCPClient (1 jour)

  1. TI_CLIENT_001 a TI_CLIENT_015

Sprint 5: Tests Modules (3-4 jours)

  1. SchedulerModule (10 TI)
  2. NotificationModule (10 TI)
  3. MonitoringModule (10 TI)
  4. AIModule (10 TI)
  5. VoiceModule (10 TI)
  6. StorageModule (10 TI)

Metriques de Succes

  • 110 tests implementes
  • Couverture > 80% pour chaque module
  • Tous les tests passent en CI
  • Temps d'execution < 30 secondes
  • Aucune dependance externe (sauf Python pour mock MCP)

Commandes de Build et Execution

# Build complet avec tests
cmake -B build -DBUILD_TESTING=ON
cmake --build build

# Executer tous les tests
cmake --build build --target test_all

# Executer tests specifiques
./build/aissia_tests "[scheduler]"      # Tests scheduler
./build/aissia_tests "[mcp]"            # Tests MCP
./build/aissia_tests "[integration]"    # Tous les TI

# Avec verbose
./build/aissia_tests -s -d yes