forked from HAL9000/cleveragents-core
5a3678cd6c
The record_usage method performed an unprotected read-modify-write on _daily_costs, a classic TOCTOU race condition. In multi-threaded execution (ThreadPoolExecutor for parallel plan steps), two threads could interleave their .get() read and = write, causing one thread's cost increment to be silently overwritten by the other. Changes: - Add _daily_costs_lock: threading.Lock to CostTracker.__init__ - Wrap the read-modify-write in record_usage with self._daily_costs_lock - Protect reads in check_daily_budget and get_daily_spend with the same lock - Add Behave @concurrency scenario: 20 threads concurrently call record_usage; final daily spend must equal the sum of all individual costs - Add Robot Framework integration smoke test via helper_cost_controls.py cost-tracker-concurrent command All accesses to _daily_costs are now lock-protected. nox -e typecheck passes with zero errors. Existing cost_controls tests continue to pass. ISSUES CLOSED: #1919
81 lines
3.4 KiB
Plaintext
81 lines
3.4 KiB
Plaintext
*** Settings ***
|
|
Resource ${CURDIR}/common.resource
|
|
Library OperatingSystem
|
|
Library Process
|
|
|
|
*** Variables ***
|
|
${PYTHON} python
|
|
${SRC_DIR} ${CURDIR}/..
|
|
|
|
*** Test Cases ***
|
|
Cost Entry Creation And Estimation
|
|
[Documentation] Verify CostEntry creation and cost estimation
|
|
[Tags] cost_controls provider model
|
|
${result}= Run Process ${PYTHON} robot/helper_cost_controls.py cost-entry
|
|
... cwd=${SRC_DIR}
|
|
Log Process Failure ${result}
|
|
Should Be Equal As Integers ${result.rc} 0
|
|
Should Contain ${result.stdout} cost-entry-ok
|
|
|
|
Provider Cost Table Lookup
|
|
[Documentation] Verify ProviderCostTable model lookup
|
|
[Tags] cost_controls provider model
|
|
${result}= Run Process ${PYTHON} robot/helper_cost_controls.py cost-table
|
|
... cwd=${SRC_DIR}
|
|
Log Process Failure ${result}
|
|
Should Be Equal As Integers ${result.rc} 0
|
|
Should Contain ${result.stdout} cost-table-ok
|
|
|
|
Cost Tracker Budget Enforcement
|
|
[Documentation] Verify CostTracker budget enforcement
|
|
[Tags] cost_controls provider budget
|
|
${result}= Run Process ${PYTHON} robot/helper_cost_controls.py cost-tracker
|
|
... cwd=${SRC_DIR}
|
|
Log Process Failure ${result}
|
|
Should Be Equal As Integers ${result.rc} 0
|
|
Should Contain ${result.stdout} cost-tracker-budget-ok
|
|
|
|
Fallback Selector No Providers
|
|
[Documentation] Verify FallbackSelector with no configured providers
|
|
[Tags] cost_controls provider fallback
|
|
${result}= Run Process ${PYTHON} robot/helper_cost_controls.py fallback-selector
|
|
... cwd=${SRC_DIR}
|
|
Log Process Failure ${result}
|
|
Should Be Equal As Integers ${result.rc} 0
|
|
Should Contain ${result.stdout} fallback-selector-ok
|
|
|
|
Settings Cost Control Defaults
|
|
[Documentation] Verify Settings cost control config defaults
|
|
[Tags] cost_controls config settings
|
|
${result}= Run Process ${PYTHON} robot/helper_cost_controls.py settings-defaults
|
|
... cwd=${SRC_DIR}
|
|
Log Process Failure ${result}
|
|
Should Be Equal As Integers ${result.rc} 0
|
|
Should Contain ${result.stdout} settings-defaults-ok
|
|
|
|
Cost Metadata Display Dict
|
|
[Documentation] Verify CostMetadata display dictionary
|
|
[Tags] cost_controls provider model
|
|
${result}= Run Process ${PYTHON} robot/helper_cost_controls.py cost-metadata
|
|
... cwd=${SRC_DIR}
|
|
Log Process Failure ${result}
|
|
Should Be Equal As Integers ${result.rc} 0
|
|
Should Contain ${result.stdout} cost-metadata-display-ok
|
|
|
|
Cost Tracker Concurrent Record Usage Is Thread Safe
|
|
[Documentation] Verify CostTracker.record_usage is thread-safe: concurrent calls must not lose cost increments
|
|
[Tags] cost_controls provider concurrency
|
|
${result}= Run Process ${PYTHON} robot/helper_cost_controls.py cost-tracker-concurrent
|
|
... cwd=${SRC_DIR}
|
|
Log Process Failure ${result}
|
|
Should Be Equal As Integers ${result.rc} 0
|
|
Should Contain ${result.stdout} cost-tracker-concurrent-ok
|
|
|
|
*** Keywords ***
|
|
Log Process Failure
|
|
[Arguments] ${result}
|
|
Run Keyword If ${result.rc} == 0 Return From Keyword
|
|
Log To Console Process failed with rc=${result.rc}
|
|
Log To Console STDOUT:${\n}${result.stdout}
|
|
Log To Console STDERR:${\n}${result.stderr}
|