diff --git a/README.md b/README.md index 8544e74..5ef90f2 100644 --- a/README.md +++ b/README.md @@ -14,3 +14,27 @@ The codebase is designed for fast, lightweight performance with an offline-first ## Theme & Styling The app adopts the brand guidelines of Zync: Deep Slate background (`#060B14`), card layouts (`#0D1421`), and cyan/brand blue highlights (`#1A8FD1`). + +## Testing Strategy + +We implement a custom, decoupled unit testing framework integrated directly into the LÖVE lifecycle. + +### Phased Testing Structure + +1. **Persistence Phase**: Validates that filesystem utility methods correctly write and load structured JSON cache files. + - *Test file*: [test_file_manager.lua](file:///C:/Users/Chitkul%20Lakshya/.gemini/antigravity/brain/3be101ac-c6b9-4c2f-bf9e-330790a2d575/scratch/temp-lua-work/src/tests/test_file_manager.lua) +2. **API Mocking Phase**: Employs decoupling mocks ([mocks.lua](file:///C:/Users/Chitkul%20Lakshya/.gemini/antigravity/brain/3be101ac-c6b9-4c2f-bf9e-330790a2d575/scratch/temp-lua-work/src/tests/mocks.lua)) to inject a mock HTTP client, validating url endpoint binding, API parameters, and response caching. + - *Test file*: [test_project_manager.lua](file:///C:/Users/Chitkul%20Lakshya/.gemini/antigravity/brain/3be101ac-c6b9-4c2f-bf9e-330790a2d575/scratch/temp-lua-work/src/tests/test_project_manager.lua) +3. **Session Store Phase**: Validates authentication managers load and dump active token credentials cleanly. + - *Test file*: [test_auth_manager.lua](file:///C:/Users/Chitkul%20Lakshya/.gemini/antigravity/brain/3be101ac-c6b9-4c2f-bf9e-330790a2d575/scratch/temp-lua-work/src/tests/test_auth_manager.lua) +4. **Connectivity Check Phase**: Validates socket verification routing and exception handling. + - *Test file*: [test_connectivity_check.lua](file:///C:/Users/Chitkul%20Lakshya/.gemini/antigravity/brain/3be101ac-c6b9-4c2f-bf9e-330790a2d575/scratch/temp-lua-work/src/tests/test_connectivity_check.lua) + +### Running Tests + +To run the phased unit testing suite using the LÖVE CLI runner, pass the `--test` flag: +```bash +love src/ --test +``` +All tests are managed by [runner.lua](file:///C:/Users/Chitkul%20Lakshya/.gemini/antigravity/brain/3be101ac-c6b9-4c2f-bf9e-330790a2d575/scratch/temp-lua-work/src/tests/runner.lua) which reports test run status and exit codes. + diff --git a/src/api/project_manager.lua b/src/api/project_manager.lua index b0d7d62..a807663 100644 --- a/src/api/project_manager.lua +++ b/src/api/project_manager.lua @@ -1,9 +1,11 @@ -local http = require("api.http_client") +local default_http = require("api.http_client") local file_manager = require("utils.file_manager") -local project_manager = {} +local project_manager = { + http = default_http +} function project_manager.fetch_projects() - local response = http.request("https://zync-pd9r.onrender.com/api/projects", "GET") + local response = project_manager.http.request("https://zync-pd9r.onrender.com/api/projects", "GET") if response.status == 200 then file_manager.save_json("projects_cache.json", response.body) return true diff --git a/src/api/task_manager.lua b/src/api/task_manager.lua index 99c67c8..d1c7341 100644 --- a/src/api/task_manager.lua +++ b/src/api/task_manager.lua @@ -1,10 +1,12 @@ -local http = require("api.http_client") +local default_http = require("api.http_client") local file_manager = require("utils.file_manager") -local task_manager = {} +local task_manager = { + http = default_http +} function task_manager.fetch_tasks(project_id) local url = "https://zync-pd9r.onrender.com/api/tasks?projectId=" .. project_id - local response = http.request(url, "GET") + local response = task_manager.http.request(url, "GET") if response.status == 200 then file_manager.save_json("tasks_" .. project_id .. ".json", response.body) return true diff --git a/src/main.lua b/src/main.lua index a7d7520..ccef747 100644 --- a/src/main.lua +++ b/src/main.lua @@ -1,4 +1,13 @@ -function love.load() +function love.load(arg) + -- Check if testing flag is passed + if arg then + for _, val in ipairs(arg) do + if val == "--test" then + require("tests.runner") + return + end + end + end print("ZYNC Desktop Loaded") end function love.draw() diff --git a/src/tests/mocks.lua b/src/tests/mocks.lua new file mode 100644 index 0000000..9b57f89 --- /dev/null +++ b/src/tests/mocks.lua @@ -0,0 +1,23 @@ +local mocks = {} + +function mocks.create_http_client() + local client = { + should_fail = false, + response_status = 200, + response_body = "{}", + calls = {} + } + + function client.request(url, method, body, headers) + table.insert(client.calls, { url = url, method = method, body = body, headers = headers }) + if client.should_fail then + return { status = 500, body = "Error" } + else + return { status = client.response_status, body = client.response_body } + end + end + + return client +end + +return mocks diff --git a/src/tests/runner.lua b/src/tests/runner.lua new file mode 100644 index 0000000..56594cf --- /dev/null +++ b/src/tests/runner.lua @@ -0,0 +1,47 @@ +local test_file_manager = require("tests.test_file_manager") +local test_project_manager = require("tests.test_project_manager") +local test_auth_manager = require("tests.test_auth_manager") +local test_connectivity_check = require("tests.test_connectivity_check") + +print("==================================================") +print("ZYNC Desktop (Lua) Phased Unit Tests Runner") +print("==================================================") + +local suites = { + { name = "FileManager persistence tests", suite = test_file_manager }, + { name = "ProjectManager API tests", suite = test_project_manager }, + { name = "AuthManager session tests", suite = test_auth_manager }, + { name = "ConnectivityCheck socket tests", suite = test_connectivity_check } +} + +local passed = 0 +local failed = 0 + +for _, item in ipairs(suites) do + print("Running Suite: " .. item.name .. "...") + local ok, err = pcall(item.suite.run) + if ok then + print("✓ " .. item.name .. " PASSED\n") + passed = passed + 1 + else + print("❌ " .. item.name .. " FAILED") + print("Error: " .. tostring(err) .. "\n") + failed = failed + 1 + end +end + +print("==================================================") +print("Test Run Summary:") +print("Passed: " .. passed) +print("Failed: " .. failed) +print("==================================================") + +-- Clean up any test files left +os.remove("test.json") +os.remove("projects_cache.json") + +if failed > 0 then + os.exit(1) +else + os.exit(0) +end diff --git a/src/tests/test_auth_manager.lua b/src/tests/test_auth_manager.lua new file mode 100644 index 0000000..cc9bcd4 --- /dev/null +++ b/src/tests/test_auth_manager.lua @@ -0,0 +1,20 @@ +local auth_manager = require("api.auth_manager") + +local function run_tests() + local test_token = "zync_test_session_token_12345" + + -- Save token + local saved = auth_manager.save_token(test_token) + assert(saved == true, "Auth token save failed") + + -- Load token + local loaded = auth_manager.get_token() + assert(loaded == test_token, "Auth token loaded mismatch") + + -- Clean up session file + os.remove("session.json") + + print("All auth_manager tests passed!") +end + +return { run = run_tests } diff --git a/src/tests/test_connectivity_check.lua b/src/tests/test_connectivity_check.lua new file mode 100644 index 0000000..92059ce --- /dev/null +++ b/src/tests/test_connectivity_check.lua @@ -0,0 +1,18 @@ +local connectivity_check = require("utils.connectivity_check") + +local function run_tests() + -- Check that the function exists + assert(type(connectivity_check.is_backend_online) == "function", "is_backend_online should be a function") + + -- Wrap in pcall since luasocket might not be present in CLI test run environment + local status, result = pcall(connectivity_check.is_backend_online) + if status then + assert(type(result) == "boolean", "is_backend_online must return a boolean") + else + print("luasocket not available, skipped socket runtime check: " .. tostring(result)) + end + + print("All connectivity_check tests passed!") +end + +return { run = run_tests } diff --git a/src/tests/test_project_manager.lua b/src/tests/test_project_manager.lua new file mode 100644 index 0000000..c77860b --- /dev/null +++ b/src/tests/test_project_manager.lua @@ -0,0 +1,32 @@ +local project_manager = require("api.project_manager") +local mocks = require("tests.mocks") +local file_manager = require("utils.file_manager") + +local function run_tests() + local mock_http = mocks.create_http_client() + project_manager.http = mock_http + + -- Test case 1: Successful fetch + mock_http.response_body = '[{"id": "p1", "name": "Test project"}]' + mock_http.response_status = 200 + mock_http.should_fail = false + + local success = project_manager.fetch_projects() + assert(success == true, "Project fetch failed on successful mock request") + assert(#mock_http.calls == 1, "Expected exactly 1 HTTP call") + assert(mock_http.calls[1].method == "GET", "Expected GET request method") + assert(mock_http.calls[1].url == "https://zync-pd9r.onrender.com/api/projects", "Target URL mismatch") + + -- Verify cache file was written + local cached = file_manager.load_json("projects_cache.json") + assert(cached ~= nil, "Cache JSON file was not written or loaded") + + -- Test case 2: Server failure + mock_http.should_fail = true + local fail_success = project_manager.fetch_projects() + assert(fail_success == false, "Project fetch should return false on server error") + + print("All project_manager tests passed!") +end + +return { run = run_tests }