Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 80 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ jobs:
name: Portable / Python ${{ matrix.python-version }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12"]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]

steps:
- uses: actions/checkout@v6
Expand All @@ -35,6 +36,84 @@ jobs:
--ignore=src/session_recall/codex_fix/tests/test_v5_sandbox.py
--ignore=src/session_recall/codex_fix/tests/test_v5_workflow.py

windows-test:
name: Windows / Python 3.12
runs-on: windows-latest

steps:
- uses: actions/checkout@v6

- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: "3.12"

- name: Install package
run: pip install -e ".[dev]"

- name: Lint with ruff
run: ruff check src/

# The Codex launcher and fixer are POSIX-only (deploy/install-codex.md);
# native Windows covers recall for Copilot CLI and Claude Code.
- name: Run tests
shell: bash
run: >-
pytest src/ -q -W error
--ignore=src/session_recall/codex_fix
--ignore=src/session_recall/providers/codex

package-smoke:
name: Package / wheel install
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v6

- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: "3.12"

- name: Build wheel
run: |
pip install build
python -m build --wheel

- name: Install wheel into a clean environment
run: |
python -m venv "$RUNNER_TEMP/pkg"
"$RUNNER_TEMP/pkg/bin/pip" install dist/*.whl

- name: Check console scripts and bundled data
env:
SESSION_RECALL_ENABLE_CLAUDE_BACKEND: "1"
run: |
set -euo pipefail
expected="$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")"
src_pyz="$(ls src/session_recall/codex_fix/data/artifacts/*.pyz | wc -l)"
bin="$RUNNER_TEMP/pkg/bin"
cd "$RUNNER_TEMP"
for cmd in session-recall session-recall-cc session-recall-claude \
session-recall-codex session-recall-codex-fix; do
"$bin/$cmd" --help > /dev/null
done
test "$("$bin/session-recall" --version)" = "auto-memory $expected"
test "$("$bin/session-recall-codex" --version)" = "$expected"
test "$("$bin/session-recall-codex-fix" --version)" = "$expected"
"$bin/python" - "$src_pyz" <<'EOF'
import sys
from importlib.resources import files
fix = files("session_recall.codex_fix") / "data"
codex = files("session_recall.providers.codex") / "verifications"
for f in (fix / "catalogue-v1.json", fix / "model-policy-v3.json",
codex / "captured-profiles.json"):
assert f.is_file(), f"missing from wheel: {f}"
pyz = [p for p in (fix / "artifacts").iterdir() if p.name.endswith(".pyz")]
assert len(pyz) == int(sys.argv[1]), f"wheel has {len(pyz)} .pyz, source has {sys.argv[1]}"
print(f"package data ok ({len(pyz)} artifacts)")
EOF

v5-stage-c-macos:
name: V5 Stage C / macOS ARM64
runs-on: macos-15
Expand Down
5 changes: 3 additions & 2 deletions src/session_recall/tests/e2e/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import sqlite3
import subprocess
import sys
import tempfile
from datetime import datetime, timedelta, timezone

import pytest
Expand Down Expand Up @@ -244,7 +245,7 @@ def empty_db(tmp_path_factory):
def run_cli(*args: str, db_path: str | None = None) -> subprocess.CompletedProcess:
"""Invoke session-recall as a subprocess with optional DB override.

Runs from /tmp to avoid auto-detection of the current git repo,
Runs from the system temp dir to avoid auto-detection of the current git repo,
ensuring the fixture DB's repos are visible without --repo filtering.
"""
env = os.environ.copy()
Expand All @@ -257,7 +258,7 @@ def run_cli(*args: str, db_path: str | None = None) -> subprocess.CompletedProce
text=True,
timeout=30,
env=env,
cwd="/tmp",
cwd=tempfile.gettempdir(),
)


Expand Down
10 changes: 9 additions & 1 deletion src/session_recall/tests/e2e/test_e2e_health_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import os
import subprocess
import sys
import tempfile

import pytest


def _run_health(*extra_args: str, env_override: dict[str, str] | None = None):
Expand All @@ -18,10 +21,15 @@ def _run_health(*extra_args: str, env_override: dict[str, str] | None = None):
text=True,
timeout=30,
env=env,
cwd="/tmp",
cwd=tempfile.gettempdir(),
)


# Human-readable health prints zone emoji; Windows' default cp1252 pipe
# encoding cannot encode them. Strict so CI flags this once it is fixed.
@pytest.mark.xfail(
sys.platform == "win32", reason="health emoji on cp1252 stdout", strict=True
)
def test_e2e_health_default_still_works():
result = _run_health()
assert result.returncode == 0, f"stderr: {result.stderr}"
Expand Down
51 changes: 51 additions & 0 deletions src/session_recall/tests/test_copilot_cli_labels.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Tests for Copilot CLI workspace labels and repo detection."""

from __future__ import annotations

import sys
from pathlib import Path

import pytest

from session_recall.providers.copilot_cli import _labels

posix_only = pytest.mark.skipif(
sys.platform == "win32", reason="POSIX path rendering"
)


@pytest.mark.parametrize("value", [None, ""])
def test_label_empty_path_returns_none(value) -> None:
assert _labels._local_workspace_label(value) is None


@posix_only
def test_label_absolute_path_is_unchanged() -> None:
assert _labels._local_workspace_label("/work/app") == "local:/work/app"


@posix_only
def test_label_relative_path_keeps_forward_slashes() -> None:
assert _labels._local_workspace_label("projects/app") == "local:projects/app"


@posix_only
def test_label_expands_home(monkeypatch, tmp_path: Path) -> None:
monkeypatch.setenv("HOME", str(tmp_path))
assert _labels._local_workspace_label("~/app") == f"local:{tmp_path}/app"


def test_detect_repo_from_directory(monkeypatch, tmp_path: Path) -> None:
monkeypatch.setattr(
_labels, "detect_repo_for_cwd", lambda p: "o/r" if p == str(tmp_path) else None
)
assert _labels._detect_repo_for_path(str(tmp_path)) == "o/r"


def test_detect_repo_from_file_uses_its_directory(monkeypatch, tmp_path: Path) -> None:
target = tmp_path / "notes.md"
target.write_text("x", encoding="utf-8")
monkeypatch.setattr(
_labels, "detect_repo_for_cwd", lambda p: "o/r" if p == str(tmp_path) else None
)
assert _labels._detect_repo_for_path(str(target)) == "o/r"
11 changes: 11 additions & 0 deletions src/session_recall/tests/test_provider_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
from __future__ import annotations

import json
import sys
from pathlib import Path

import pytest

from session_recall.providers.copilot_cli import CopilotCliProvider
from session_recall.providers.file import (
VSCodeProvider,
Expand All @@ -13,6 +16,12 @@
_is_wsl,
)

# Windows Python rewrites POSIX/WSL session paths (/home/x -> \home\x).
# Fixed by PR #29; strict so CI flags these once the fix lands.
posix_paths_on_windows = pytest.mark.xfail(
sys.platform == "win32", reason="POSIX paths rewritten on Windows (#29)", strict=True
)


def test_vscode_kind1_inputtext_extraction() -> None:
row = {
Expand Down Expand Up @@ -126,6 +135,7 @@ def test_cli_fallback_prefers_context_repository_field(tmp_path: Path) -> None:
assert results[0]["repository"] == "dezgit2025/auto-memory"


@posix_paths_on_windows
def test_cli_fallback_uses_tool_path_to_infer_repository(
monkeypatch, tmp_path: Path
) -> None:
Expand Down Expand Up @@ -176,6 +186,7 @@ def test_cli_fallback_uses_tool_path_to_infer_repository(
assert sessions[0]["repository"] == "dezgit2025/auto-memory"


@posix_paths_on_windows
def test_cli_fallback_labels_non_repo_session_as_local_workspace(
tmp_path: Path,
) -> None:
Expand Down
Loading