This document outlines the testing flow executed by the task/tests/main.test.ts Jest test suite. This test is an integration test that simulates the full lifecycle of a Koii task, including setup, task execution, submission, auditing, and distribution.
The test suite is structured using Jest, with a beforeAll block for initial setup, several it blocks for individual test cases, and an afterAll block for cleanup.
namespaceWrapper.defaultTaskSetup(): This function is called first. It likely initializes the core environment for the task, potentially setting up mock storage, a mock K2 connection, or other foundational elements required for the Koii task SDK to operate in a test environment.initializeTaskManager(...): This function initializes the Koii task manager with the specific logic for this task. It takes an object with the following functions as arguments:setup: Points to thesetupfunction from../src/task/0-setup.ts.task: Points to thetaskfunction from../src/task/1-task.ts.submission: Points to thesubmissionfunction from../src/task/2-submission.ts.audit: Points to theauditfunction from../src/task/3-audit.ts.distribution: Points to thedistributionfunction from../src/task/4-distribution.ts.routes: Points to theroutesfunction from../src/task/5-routes.ts.- This step essentially configures the task manager with the actual code that will be executed during different phases of the task lifecycle.
The following test cases are executed sequentially:
-
should perform the core logic task- Sets the current
roundto 1. - Calls
taskRunner.task(round): This executes the main task logic defined insrc/task/1-task.ts(or, more accurately, the cron job logic insrc/task/0-setup.tswhich1-task.tsmight be a placeholder for, as noted intask/README.md). This involves:- Fetching a "todo" from the
middle-server(which needs to be running or mocked). - Delegating this "todo" to the Python Orca pod simulator.
- The Python pod processes it and sends the result back to a route on the Koii task's internal server (defined in
src/task/5-routes.ts). - This route handler uploads the result to IPFS and notifies the
middle-server.
- Fetching a "todo" from the
await namespaceWrapper.storeGet("value"): Attempts to retrieve a value named "value" from the namespace's key-value store. This "value" is expected to be set during the task execution (likely the result from the Python pod or the IPFS CID).- Asserts that the retrieved
valueis defined and not null.
- Sets the current
-
should make the submission to k2 for dummy round 1- Sets the current
roundto 1. - Calls
await taskRunner.submitTask(round): This executes the submission logic defined insrc/task/2-submission.ts. This function is responsible for preparing and returning the submission data (e.g., the IPFS CID of the result) that would be sent to the K2 layer. await namespaceWrapper.getTaskState({}): Retrieves the current state of the task from the namespace.- Uses
Joito define a schema for the expected structure oftaskState?.submissions. - Validates
taskState?.submissionsagainst this schema. - Asserts that there are no validation errors, meaning a submission with the correct structure was made and recorded in the task state.
- Sets the current
-
should make an audit on submission- Sets the current
roundto 1. - Calls
await taskRunner.auditTask(round): This executes the audit logic defined insrc/task/3-audit.ts. This function is called to validate submissions from other nodes. await namespaceWrapper.getTaskState({}): Retrieves the task state.- Uses
Joito define a schema fortaskState?.submissions_audit_trigger. - Validates the audit trigger data against the schema.
- Asserts no validation errors.
- Sets the current
-
should make the distribution submission to k2 for dummy round 1- Sets the current
roundto 1. - Calls
await taskRunner.submitDistributionList(round): This executes the logic to prepare and submit the distribution list (who gets rewards). await namespaceWrapper.getTaskState({}): Retrieves the task state.- Uses
Joito define a schema fortaskState?.distribution_rewards_submission. - Validates the distribution submission data.
- Asserts no validation errors.
- Sets the current
-
should make an audit on distribution submission- Sets the current
roundto 1. - Calls
await taskRunner.auditDistribution(round): Executes logic to audit the distribution lists submitted by other nodes. await namespaceWrapper.getTaskState({}): Retrieves the task state.- Uses
Joito define a schema fortaskState?.distributions_audit_trigger. - Validates the distribution audit trigger data.
- Asserts no validation errors.
- Sets the current
-
should make sure the submitted distribution list is valid- Sets the current
roundto 1. await namespaceWrapper.getDistributionList("", round): Retrieves the generated distribution list for the current round.- Uses
Joito define a schema for the distribution list itself (a map of public keys to reward amounts). - Validates the parsed distribution list.
- Asserts no validation errors.
- Sets the current
-
should test the endpoint- Makes an HTTP GET request to
http://localhost:3000(the Koii task's internal Express server, started bynamespaceWrapper.defaultTaskSetup()). - Asserts that the HTTP status is 200.
- Asserts that the response body is
{ message: "Running", status: 200 }.
- Makes an HTTP GET request to
-
should generate an empty distribution list when submission is 0- This is a unit test for the
distributionfunction itself (src/task/4-distribution.ts). - Calls
distribution([], bounty, roundNumber)with an empty array of submitters. - Asserts that the returned distribution list is an empty object
{}.
- This is a unit test for the
-
should generate a distribution list contains all the submitters- Another unit test for the
distributionfunction. - Creates a list of 5 mock
Submitterobjects with random vote and stake values. - Calls
distribution(submitters, bounty, roundNumber). - Asserts that the number of keys in the returned distribution list is equal to the number of mock submitters.
- Asserts that the public keys in the distribution list match the public keys of the mock submitters.
- Another unit test for the
_server.close(): This closes the Express server that was started bynamespaceWrapper.defaultTaskSetup()as part of the testing environment. This is important to free up the port (default 3000) and allow subsequent test runs or other applications to use it.
The main.test.ts is an integration test that simulates many parts of a real Koii task interacting with external (simulated) components. Potential reasons for slowness or hanging include:
- Full Task Lifecycle Simulation: The test runs through
task,submitTask,auditTask,submitDistributionList, andauditDistribution. Each of these steps can involve significant computation, state changes, and potentially I/O operations (even if mocked). - Dependencies on External Services (Simulated):
- Middle Server: The
taskRunner.task()step relies on fetching data from themiddle-server. If the actualmiddle-serverisn't running and correctly configured (e.g., onhttp://localhost:5001as per our previous fixes), or if there's no mock in place for this test, this step could hang or timeout waiting for a response. - Python Orca Pod: The task delegates computation to a Python server.
initializeOrcaClientForTestingstarts this Python server as a child process. If there are issues starting the Python server (e.g.,python3not found, Flask not installed correctly in the environment the child process inherits, errors inapp.py), or if communication with it fails, the test could hang. - IPFS Operations: The
5-routes.tshandler callsstoreFilewhich interacts with@_koii/storage-task-sdk. While this might be mocked or use local storage for tests, any real network interaction or significant local file I/O could add time.
- Middle Server: The
namespaceWrapperInternals: ThenamespaceWrapperfunctions (e.g.,storeGet,storeSet,getTaskState,defaultTaskSetup) manage the state of the task and its interaction with the (mocked) Koii environment. These operations, especiallydefaultTaskSetupwhich might initialize an Express server, can take time.- Timeouts: Jest tests have default timeout limits. If any of the asynchronous operations within a test case (especially those involving external processes or simulated network calls) take longer than this timeout, Jest will interrupt the test, which might be perceived as a hang followed by a failure.
- Sequential Execution and State: The tests are largely sequential and build on the state created by previous tests (e.g., a submission must exist before it can be audited). If an early test fails to set up the correct state, later tests might behave unpredictably or take longer trying to operate on invalid state.
- Resource Contention: Running multiple servers (Node.js task server, Python Flask server, potentially a separate middle server if not mocked directly in tests) can consume system resources, slowing down operations.
- Cleanup in
afterAll: The_server.close()is crucial. If this fails or is not reached (e.g., due to a test hanging indefinitely beforeafterAll), the port might remain occupied, causing issues for subsequent test runs.
Given the ENOENT error for python we saw earlier (and fixed to python3), issues with the Python child process startup were a likely culprit for previous hangs. The port conflict for the middle-server was another. Even with these fixed, the inherent complexity of simulating the entire task lifecycle means these tests can be slower than simple unit tests.