Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
385504c
feat: add CI coverage gate, data-model sync, contribution docs, and r…
Senorespecial Jul 17, 2026
06b0076
feat: add CI coverage gate, data model enums, and README data model ref
Senorespecial Jul 17, 2026
7406323
feat(ci): add auto-generated ER diagram via erdia
Senorespecial Jul 17, 2026
3fd8ff1
fix(ci): use explicit user/db in PostgreSQL health check for erdia job
Senorespecial Jul 17, 2026
d604dc8
feat(ci): add Codecov integration for contracts and backend coverage
Senorespecial Jul 17, 2026
3529685
fix(ci): correct Codecov backend coverage path from backend/ working dir
Senorespecial Jul 17, 2026
edc46d3
feat(ci): add Vitest + coverage for frontend, upload to Codecov
Senorespecial Jul 17, 2026
cd8f233
docs: add Codecov coverage badges for contracts and backend to README
Senorespecial Jul 17, 2026
a2c6c71
fix(docs): use dynamic Codecov badges instead of hardcoded percentages
Senorespecial Jul 17, 2026
6956b6e
docs: add Codecov badge for frontend coverage flag
Senorespecial Jul 17, 2026
9b1032f
fix(ci): set fail_ci_if_error to true for all Codecov upload steps
Senorespecial Jul 17, 2026
b72e5d2
docs: add Code Coverage section to README explaining badges and dashb…
Senorespecial Jul 17, 2026
ed02c5b
Merge remote-tracking branch 'upstream/main' into feat/ci-coverage-an…
Senorespecial Jul 17, 2026
4737c37
fix(codecov): adjust coverage thresholds to match actual current levels
Senorespecial Jul 17, 2026
a726e2a
docs: add Code Coverage section to CONTRIBUTING.md
Senorespecial Jul 17, 2026
0cae496
fix(docs): add --all flag to contracts coverage command in CONTRIBUTI…
Senorespecial Jul 17, 2026
176347c
test(backend): add unit tests for auth and payments modules
Senorespecial Jul 17, 2026
cb3b37e
test(auth): merge resetPassword edge cases into existing describe block
Senorespecial Jul 17, 2026
744de0c
test(workspace-tracking): add unit tests for providers, service, and …
Senorespecial Jul 17, 2026
3e5fdeb
fix(codecov): bump backend coverage target from 10% to 30%
Senorespecial Jul 17, 2026
b53c856
test(workspaces): add unit tests for service and all 6 providers
Senorespecial Jul 17, 2026
090ac18
test(bookings): add unit tests for service, providers, controller, an…
Senorespecial Jul 17, 2026
d835067
test(notifications): add unit tests for providers, service, controlle…
Senorespecial Jul 17, 2026
0df144f
test(invoices): add unit tests for providers, service, and controller
Senorespecial Jul 17, 2026
6e282b5
test(cloudinary): add unit tests for CloudinaryService
Senorespecial Jul 17, 2026
5fd45c7
test(backend): add unit tests for 7 modules + fix audit-log interceptor
Senorespecial Jul 19, 2026
e50ca3e
test(frontend): add UI component tests and configure vitest coverage
Senorespecial Jul 19, 2026
6bea0ae
chore(codecov): reduce patch coverage target to 50%
Senorespecial Jul 19, 2026
fa28e75
chore(CI): fix whitespace in coverage workflow
Senorespecial Jul 19, 2026
b131061
chore(backend): apply prettier formatting to test files and config
Senorespecial Jul 19, 2026
f79bbb3
test(backend): add unit tests for 11 providers (payments + auth)
Senorespecial Jul 19, 2026
f632799
test(backend): add tests for JWT strategy and soroban-utils (100% cov…
Senorespecial Jul 19, 2026
99d1b70
test(frontend): add tests for hooks, providers, store, and query keys
Senorespecial Jul 19, 2026
70676df
test(frontend): add tests for form, table, label, and Navbar components
Senorespecial Jul 19, 2026
4b8d928
test(frontend): boost env.ts coverage from 57% to 100% with edge case…
Senorespecial Jul 19, 2026
b6e6c93
test(frontend): add comprehensive useAuthRedirect hook tests (235 tests)
Senorespecial Jul 19, 2026
f0c0da4
test(frontend): boost storage.ts coverage from 75% to 100% with SSR s…
Senorespecial Jul 19, 2026
82358f6
test(frontend): add tests for useLoginUser and useRegisterUser react-…
Senorespecial Jul 19, 2026
ea295bc
test(frontend): add tests for all 20 react-query hooks (auth, 2fa, wo…
Senorespecial Jul 19, 2026
9dd7188
feat: finalize CI coverage gates, data model docs, threat model, and …
Senorespecial Jul 20, 2026
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
308 changes: 306 additions & 2 deletions .github/workflows/CI.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,153 @@ jobs:
run: cargo build --all --verbose

# ─────────────────────────────────────────────
coverage:
name: Contract Coverage (>= 80%)
runs-on: ubuntu-latest
needs: build
defaults:
run:
working-directory: contracts
env:
CARGO_TERM_COLOR: always
RUSTUP_TOOLCHAIN: stable
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain (with LLVM tools for tarpaulin)
uses: dtolnay/rust-toolchain@stable
with:
components: llvm-tools-preview
- name: Install cargo-binstall
run: curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
- name: Install cargo-tarpaulin (pre-built binary)
run: cargo binstall cargo-tarpaulin -y
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
contracts/target
key: ${{ runner.os }}-cargo-coverage-${{ hashFiles('''contracts/Cargo.lock''') }}
restore-keys: |
${{ runner.os }}-cargo-coverage-
${{ runner.os }}-cargo-
- name: Run per-crate coverage (fail under 80%)
run: |
set -euo pipefail
mkdir -p ../coverage/contracts
echo "# Contract coverage report" > ../coverage/contracts/SUMMARY.md
OVERALL_STATUS=0
# Soroban contracts compile to wasm32/cdylib but their tests run on
# the host (x86_64). `--engine llvm` instruments the host test
# binaries; requires the llvm-tools-preview component installed above.
for crate in access_control common_types manage_hub membership_token payment_escrow resource_credits workspace_booking; do
if [ ! -f "$crate/Cargo.toml" ]; then
echo "::warning::Skipping missing crate $crate"
continue
fi
echo ""
echo ""
echo "========================================================"
echo " Coverage for crate: $crate"
echo "========================================================"
if ! cargo tarpaulin \
--manifest-path "$crate/Cargo.toml" \
--engine llvm \
--timeout 300 \
--fail-under 80 \
--out Xml Stdout \
--output-dir "../coverage/contracts/$crate"; then
echo "::error file=$crate::Coverage for $crate is below 80% (or tarpaulin failed)."
OVERALL_STATUS=1
fi
done
if [ "$OVERALL_STATUS" -ne 0 ]; then
echo "::error::One or more contract crates fell below the 80% coverage threshold."
exit 1
fi
- name: Upload coverage artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: contract-coverage
path: coverage/contracts
if-no-files-found: ignore

- name: Upload coverage to Codecov (contracts)
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: ../coverage/contracts
flags: contracts
fail_ci_if_error: true
verbose: true

# ─────────────────────────────────────────────
# NO PLACEHOLDER TESTS
# Fails if any test file contains placeholder-style content or filenames.
# Placeholder tests (e.g. expect(1+1).toBe(2)) are often created as
# scaffolding and accidentally committed.
# ─────────────────────────────────────────────
no-placeholders:
name: No Placeholder Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check for placeholder test files
run: |
set -euo pipefail
HAS_PLACEHOLDER=false

# Check 1: Filename-based — flag files with "placeholder" or "template"
# in the name that look like test files.
# Exclude node_modules/ to avoid false positives from dependency files.
MATCHES_BY_NAME=$(find . -type f -not -path '*/node_modules/*' \
\( -name '*placeholder*' -o -name '*template*' \) \
\( -name '*.test.*' -o -name '*.spec.*' \) 2>/dev/null || true)

if [ -n "$MATCHES_BY_NAME" ]; then
echo "::error::Test files with placeholder/template in filename:"
echo "$MATCHES_BY_NAME" | sed 's/^/ /'
HAS_PLACEHOLDER=true
fi

# Check 2: Content-based — flag test files whose describe() or it()
# description suggests a placeholder or template.
if command -v rg &>/dev/null; then
MATCHES_BY_CONTENT=$(
rg -l \
--glob '*.test.*' \
--glob '*.spec.*' \
-e 'describe\(("placeholder"|'"'"'placeholder'"'"'|"template"|'"'"'template'"'"')' \
-e 'it\(("should pass"|'"'"'should pass'"'"'|"is a placeholder"|'"'"'is a placeholder'"'"')' \
2>/dev/null || true
)
else
# fallback to grep
MATCHES_BY_CONTENT=$(
grep -rl \
--include='*.test.*' \
--include='*.spec.*' \
-E "describe\(\"placeholder\"|describe\('placeholder'|describe\(\"template\"|describe\('template'|it\(\"should pass\"|it\('should pass'|it\(\"is a placeholder\"|it\('is a placeholder'" \
2>/dev/null || true
)
fi

if [ -n "$MATCHES_BY_CONTENT" ]; then
echo "::error::Test files with placeholder-style descriptions:"
echo "$MATCHES_BY_CONTENT" | sed 's/^/ /'
HAS_PLACEHOLDER=true
fi

if [ "$HAS_PLACEHOLDER" = true ]; then
echo ""
echo "::error::Placeholder test files detected — remove or replace them with real tests."
exit 1
fi

echo " No placeholder test files found."

# SEMGREP — Reentrancy guard enforcement
# (TEMPORARILY DISABLED — uncomment to re-enable)
# ─────────────────────────────────────────────
Expand Down Expand Up @@ -165,8 +312,17 @@ jobs:
- name: Build (TypeScript check)
run: npm run build

- name: Unit tests
run: npm run test -- --passWithNoTests
- name: Unit tests with coverage
run: npm run test:cov -- --passWithNoTests

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: coverage/lcov.info
flags: backend
fail_ci_if_error: true
verbose: true

# ─────────────────────────────────────────────
# FRONTEND — Next.js
Expand Down Expand Up @@ -196,7 +352,155 @@ jobs:
- name: Lint
run: npm run lint

- name: Tests with coverage
run: npm run test:cov

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: coverage/lcov.info
flags: frontend
fail_ci_if_error: true
verbose: true

- name: Build
run: npm run build
env:
NEXT_PUBLIC_API_URL: http://localhost:3001

# ─────────────────────────────────────────────
# DATA-MODEL SYNC CHECK
# Fails the build if any TypeORM entity file or migration is modified
# without updating backend/docs/data-model.md.
# See https://github.com/NovaCoreLabs1/NovaLabs/issues/140
# ─────────────────────────────────────────────
verify-data-model:
name: Verify Data Model Sync
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Detect entity/migration changes without doc update
env:
EVENT_NAME: ${{ github.event_name }}
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
PUSH_BASE_SHA: ${{ github.event.before }}
run: |
set -euo pipefail
# Determine which commits to diff against depending on event type.
# - For pull_request: compare PR head against the PR's base SHA.
# - For push (incl. merges): compare HEAD against the previous SHA.
# - Fallback (e.g. workflow_dispatch): skip the check.
case "$EVENT_NAME" in
pull_request)
BASE_SHA="$PR_BASE_SHA"
;;
push)
BASE_SHA="$PUSH_BASE_SHA"
;;
*)
echo "Skipping data-model sync check for event: $EVENT_NAME"
exit 0
;;
esac
if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then
echo "::warning::No base SHA available; skipping data-model sync check."
exit 0
fi
git fetch --no-tags --depth=1 origin "$BASE_SHA"
ENTITY_CHANGED="$(git diff --name-only "$BASE_SHA"...HEAD | grep -E '(^|/)entities/.*\.entity\.ts$' || true)"
MIGRATION_CHANGED="$(git diff --name-only "$BASE_SHA"...HEAD | grep -E '(^|/)migrations/.*\.ts$' || true)"
DOC_CHANGED="$(git diff --name-only "$BASE_SHA"...HEAD | grep -E '(^|/)backend/docs/data-model\.md$' || true)"
SCHEMA_CHANGED="$ENTITY_CHANGED$MIGRATION_CHANGED"
if [ -n "$SCHEMA_CHANGED" ] && [ -z "$DOC_CHANGED" ]; then
{
echo "::error::TypeORM entities or migrations were modified but backend/docs/data-model.md was not updated."
echo "::error::Files changed:"
[ -n "$ENTITY_CHANGED" ] && printf '::error file=%s::Entity file changed without docs update\n' $ENTITY_CHANGED
[ -n "$MIGRATION_CHANGED" ] && printf '::error file=%s::Migration file changed without docs update\n' $MIGRATION_CHANGED
echo "::error::Please update backend/docs/data-model.md to keep the ER diagram and field reference in sync."
}
exit 1
fi
echo "No entity/migration/doc sync issues detected."
if [ -n "$SCHEMA_CHANGED" ]; then
echo "Schema files changed (and docs updated):"
printf ' %s\n' $SCHEMA_CHANGED
fi

# ─────────────────────────────────────────────
# AUTO-GENERATE ER DIAGRAM (erdia)
# Regenerates the Mermaid ER diagram from TypeORM entities and fails
# if the generated output differs from what is committed. Developers
# run `npm run db:diagram` locally after entity changes.
# See https://github.com/NovaCoreLabs1/NovaLabs/issues/140
# ─────────────────────────────────────────────
generate-er-diagram:
name: Generate ER Diagram
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
defaults:
run:
working-directory: backend
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: novalabs
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U postgres -d novalabs"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
cache-dependency-path: backend/package-lock.json

- name: Install dependencies
run: npm ci

- name: Generate ER diagram with erdia
run: npm run db:diagram
env:
DATABASE_HOST: localhost
DATABASE_PORT: 5432
DATABASE_USERNAME: postgres
DATABASE_PASSWORD: password
DATABASE_NAME: novalabs

- name: Check generated docs match committed version
run: |
OUTPUT_DIR="docs/erd"
if [ -d "$OUTPUT_DIR" ]; then
CHANGES="$(git status --porcelain -- "$OUTPUT_DIR/" 2>/dev/null || true)"
if [ -z "$CHANGES" ]; then
echo "Generated ER diagram matches committed version."
else
echo "Generated ER diagram differs from committed version."
echo " Run 'npm run db:diagram' locally and commit the changes."
echo ""
git status -- "$OUTPUT_DIR/"
exit 1
fi
else
echo "erdia output directory not found - run erdia locally first."
echo " npm run db:diagram"
echo " Then commit the docs/erd/ directory."
fi
60 changes: 60 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# dependencies
node_modules/
.pnp
.pnp.js

# testing
coverage/
.nyc_output/

# next.js
.next/
out/

# production
build/
dist/

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# IDE
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.idea/
*.swp
*.swo
*~

# TypeScript
*.tsbuildinfo

# Stryker temp files
.stryker-tmp

# Logs
logs/
*.log

# Stryker mutation testing output
reports/

# OS files
Thumbs.db
Loading