afterhours-shift-manager/tests/scripts/test_copy_dynamodb.py
Adam Moussa 13350b72d0
Some checks are pending
Deploy / Deploy to prod (push) Waiting to run
feat(infra): migrate afterhours to HCP Terraform (PLAT-74) (#252)
* fix(cutover): write Slack secrets into empty Terraform shells

DescribeSecret succeeds on HCP-created shells with no version, so skip-if-exists left roster and Slack tokens unset.

* feat(infra): migrate afterhours to HCP Terraform (PLAT-74)

Replace the mgmt SAM stack with a prod-only HCP workspace, in-repo hcptf IAM, stub Lambdas, and zip CD on push to main.

* fix(cutover): retry DDB unprocessed items and skip past at() holidays

Unprocessed BatchWriteItem rows and leftover past at() schedules would drop roster data or abort holiday recreation during prod cutover.
2026-09-15 23:31:59 +00:00

64 lines
2 KiB
Python

"""copy_dynamodb retries UnprocessedItems instead of counting them as written."""
import importlib.util
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
def _load():
spec = importlib.util.spec_from_file_location(
"copy_dynamodb", ROOT / "scripts" / "cutover" / "copy_dynamodb.py"
)
mod = importlib.util.module_from_spec(spec)
sys.modules["copy_dynamodb"] = mod
spec.loader.exec_module(mod)
return mod
mod = _load()
class FakeDdb:
def __init__(self, unprocessed_first=None):
self.calls = []
self.unprocessed_first = list(unprocessed_first or [])
self._first = True
def batch_write_item(self, RequestItems):
batch = RequestItems[mod.TABLE]
self.calls.append(batch)
if self._first and self.unprocessed_first:
self._first = False
leftover = [req for req in batch if req in self.unprocessed_first]
return {"UnprocessedItems": {mod.TABLE: leftover} if leftover else {}}
return {"UnprocessedItems": {}}
def test_batch_write_retries_unprocessed_items():
items = [{"PK": {"S": "a"}}, {"PK": {"S": "b"}}]
first = [{"PutRequest": {"Item": items[1]}}]
client = FakeDdb(unprocessed_first=first)
written = mod._batch_write_all(client, items, sleep=lambda _s: None)
assert written == 2
assert len(client.calls) == 2
assert client.calls[1] == first
def test_batch_write_raises_if_unprocessed_remain():
items = [{"PK": {"S": "a"}}]
stuck = [{"PutRequest": {"Item": items[0]}}]
client = FakeDdb(unprocessed_first=stuck)
client._always = True
def always_unprocessed(RequestItems):
client.calls.append(RequestItems[mod.TABLE])
return {"UnprocessedItems": {mod.TABLE: stuck}}
client.batch_write_item = always_unprocessed
try:
mod._batch_write_all(client, items, sleep=lambda _s: None, max_attempts=3)
raise AssertionError("expected RuntimeError")
except RuntimeError as exc:
assert "unprocessed" in str(exc)