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
113 changes: 113 additions & 0 deletions .agents/skills/test-driven-change/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
---
name: test-driven-change
description: Deliver observable behavior changes through a verified Red–Green–Refactor cycle. Use for new features, bug fixes, domain logic, permission changes, API behavior, data changes, and contract changes; do not use automatically for documentation-only, formatting, generated-file, or mechanical changes without testable behavior.
---

# Deliver a test-driven change

## Establish the proof

Before writing the test, record:

- the domain invariant;
- the observable target behavior;
- the defect or regression the test must detect;
- the chosen test level;
- what the test proves and what it does not prove;
- relevant negative and boundary cases.

Choose the lowest level that proves the real contract without replacing
observable behavior with implementation detail. Use a higher integration,
contract, smoke, or end-to-end level when the lower level cannot exercise the
relevant boundary truthfully. For a bug fix, make the smallest proof a
regression test.

## Red

1. Write the smallest meaningful test before changing production code.
2. Run that test and record the command, exit code, and concrete failure or
assertion difference.
3. Confirm that it fails for the expected domain reason: the target behavior
is missing or wrong.
4. Do not proceed to Green when:
- the test is immediately green and is not an explicitly justified
characterization test;
- it fails only because of infrastructure, syntax, fixture, or configuration
defects;
- it does not reach the target behavior;
- it checks only a mock call although observable behavior can be checked
meaningfully.

Repair a defective test environment without adding the target production
behavior, then repeat Red. An immediately green test can preserve known
behavior as a characterization test, but is not evidence of a TDD Red step.

## Green

1. Make only the smallest production-code change needed to satisfy the test.
2. Add no speculative abstraction or unrelated refactoring.
3. Run the new test again.
4. Run the relevant existing regression suite.

## Cover risk-specific evidence

For permission changes, verify at least:

- unauthorized access is rejected;
- authorized access succeeds;
- a foreign or manipulated object ID grants no access and causes no data
change;
- a rejected request changes no data;
- UI visibility is never used as a substitute for server-side access control.

For database or migration changes, verify at least:

- fresh installation on an empty schema;
- upgrade from at least the immediately relevant prior version;
- preservation or correct migration of existing data;
- required constraints and indexes exist and preserve integrity;
- no incomplete state after failure.

For shared libraries or contracts, verify:

- relevant provider and consumer contract tests;
- every affected dependent app;
- backward compatibility or an explicitly documented break.

For write operations, verify:

- the return value or HTTP result;
- the persisted state;
- absence of unwanted side effects;
- repeated execution when idempotency is relevant.

These checks supplement applicable repository stop gates. They do not authorize
database, permission, cross-repository, or production changes.

## Refactor

Refactor only after the new test and relevant regression suite are green.
Change no domain behavior. Run the new test and relevant existing tests again
after refactoring.

## Handle non-behavioral changes and deviations

For documentation-only, formatting, generated-file, or mechanical changes
without meaningfully testable behavior, use the suitable deterministic syntax,
contract, generation, layout, link, or structure check instead of an artificial
TDD cycle.

State and justify every deviation from the test-driven workflow. Do not treat a
test that was never observed red as TDD evidence.

## Report

Include:

- the protected invariant and chosen test level;
- the initially red test and expected failure reason;
- the minimal implementation;
- executed focused, regression, and contract tests as applicable;
- concrete commands, results, and exit codes;
- remaining untested risks;
- justified deviations.
12 changes: 10 additions & 2 deletions .agents/skills/work-in-nextcloud-app/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,23 @@ Stop immediately if production systems, Git history rewriting, new production de

## Test-first and coverage

- New development and bug fixes normally follow Red – Green – Refactor. Start a bug fix with a regression test and a refactoring with characterization tests. Develop domain logic, permissions, hierarchies, conflicts, and validation test-first.
- Use the locally available sibling skill `test-driven-change` for every new feature, bug fix, domain rule, permission change, API behavior, data change, or contract change. It is the sole detailed Red–Green–Refactor workflow; this section adds only Nextcloud-app test selection and coverage requirements.
- API changes cover success, validation failure, and typical Allow/Deny cases. Cross-app contracts have provider and consumer contract tests. Use integration/DDEV tests for migrations and repository behavior when unit tests cannot represent the real contract.
- Develop executable UI logic test-first; additionally cover layout, accessibility, and Nextcloud integration with suitable smoke or browser checks.
- Permitted test-first entry exceptions are time-boxed exploratory spikes, purely declarative text/metadata or trivial presentation changes, and hard-to-isolate Nextcloud integration where a broader integration test is more truthful. Discard spike code or characterize it before adoption; give declarative changes appropriate syntax, contract, layout, or visibility checks.
- Treat a time-boxed exploratory spike or hard-to-isolate Nextcloud integration as an explicitly justified deviation. Discard spike code or characterize it before adoption; choose the truthful broader integration level when isolation would hide the real contract.
- Use the local fast entries named by `AGENTS.md`, normally `php tests/run.php` and `node tests/run-js.mjs`; dependency-light PHP smokes run in isolated processes. Run LocalBase and every affected consumer contract/smoke suite after a LocalBase contract change.
- In local pre-production, app tests may use shared LocalBase test helpers through relative repository paths. Add heavier packaging/autoload structure or a larger test framework only when path handling, runners, assertions, mocks, or fixtures are materially duplicated or impair readability.
- Known overall and app coverage must not decline unnoticed. Aim for at least 85 percent line coverage for new or materially changed executable code, report PHP and JavaScript separately, and fully cover security invariants regardless of percentages. Coverage is a warning and delivery indicator, not a substitute for meaningful assertions.
- Do not prepare a commit or release with red relevant fast tests, contract tests, security checks, coverage gates, or delivery gates.

## Persistent state and migrations

- Before implementing a feature that changes persistent domain objects, determine the complete state model: allowed and forbidden starting states, preconditions, target state, side effects, error states, retry or repetition behavior, and relevant concurrency conflicts.
- Do not expose unrestricted generic setters for status changes governed by domain transition rules. Encapsulate allowed transitions in the domain model or one clearly responsible application service and cover positive, negative, and failure cases.
- Before a database change that can encounter existing data, document the old and new schema, transformation rules, known existing-data variants, integrity conditions, transaction boundary, resumability, and rollback limits.
- Such a database change requires at least a fresh-install test, an upgrade test from the relevant previous version with synthetic existing data, domain data- and relationship-integrity checks, handling of invalid or contradictory legacy data, and an application test on the migrated schema.
- Never modify a published migration after the fact. Correct it with a new migration.

## DDEV, Nextcloud, and hosting safety

- Prefer local PHP/Node checks and batch DDEV checks. Control the shared DDEV project only from its documented `nextcloud-dev` root when that separate Parent workspace is actually available.
Expand Down
27 changes: 24 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,24 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
coverage: none
coverage: ${{ matrix.php-version == '8.3' && 'xdebug' || 'none' }}
tools: none
- name: PHP-Tests
if: matrix.php-version == '8.5'
working-directory: app
run: php tests/run.php
- name: PHP-Coverage-Tooling installieren
if: matrix.php-version == '8.3'
run: composer install --working-dir=localbase/tests/coverage --no-interaction --no-progress --prefer-dist
- name: PHP-Coverage
if: matrix.php-version == '8.3'
working-directory: app
run: |
mkdir -p "$RUNNER_TEMP/php-coverage"
PHP_COVERAGE_COMMAND="$GITHUB_WORKSPACE/localbase/tests/coverage/vendor/bin/phpcov" \
PHP_COVERAGE_OUTPUT_DIR="$RUNNER_TEMP/php-coverage" \
php tests/run.php
php "$GITHUB_WORKSPACE/localbase/tests/coverage/merge-clover.php" adcalendar "$RUNNER_TEMP/php-coverage" 58.06

javascript:
name: JavaScript
Expand Down Expand Up @@ -82,6 +95,14 @@ jobs:
with:
node-version: 24
package-manager-cache: false
- name: JavaScript-Tests
- name: JavaScript-Coverage-Tooling installieren
run: npm ci --prefix localbase/tests/coverage --ignore-scripts
- name: JavaScript-Tests mit Coverage
working-directory: app
run: node tests/run-js.mjs
run: |
../localbase/tests/coverage/node_modules/.bin/c8 \
--all \
'--include=js/**/*.js' \
--check-coverage \
--lines=53.84 \
node tests/run-js.mjs
17 changes: 8 additions & 9 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Nextcloud-App-ID:
adcalendar

Die priorisierte Produktplanung und offene Entscheidungen stehen in `ROADMAP.md`; verbindliche Fach-, Sicherheits- und Architekturregeln bleiben in dieser Datei.
Der ausführliche geltende Ist-Vertrag steht in `docs/architecture.md`; diese
Datei hält die bei jeder Arbeit benötigten Grenzen und Prüfungen.

## Zielsetzung und Fachkontext

Expand All @@ -21,7 +23,7 @@ AD Kalender uebertraegt den fachlichen Kern des bisherigen WordPress-Plugins `ad
Kernprozess:

- Eine Wochenansicht zeigt Mitarbeiter*innen als Zeilen und Kalendertage als Spalten.
- Eine umschaltbare Monatsansicht zeigt die betroffenen Wochenblöcke untereinander, behält Mitarbeiter*innen als Zeilen bei, dimmt Randtage und fixiert die Personenspalte beim horizontalen Scrollen.
- Eine umschaltbare Monatsansicht zeigt die betroffenen Wochenblöcke untereinander und unterstützt wie die Wochenansicht „Tage als Zeilen“ sowie „Personen als Zeilen“. Die zu den Personen gehörende erste Spalte beziehungsweise Kopfzeile bleibt beim Scrollen sichtbar. Randtage werden gedimmt; Samstag und Sonntag werden flächig und zusätzlich mit dem Text „Wochenende“ gekennzeichnet. Gesetzliche Feiertage der organisationsweit konfigurierten Kalenderregion werden serverseitig über den gemeinsamen read-only LocalBase-Vertrag geliefert und erscheinen mit ihrem Namen als reine Anzeigeebene. `DE`, `DE-BE` und `Europe/Berlin` bleiben Bestandsdefaults; persönliche Zeitzonen beeinflussen nur individuelle Terminanzeigen. Feiertage verändern weder Einträge noch Verfügbarkeit oder Rechte.
- Dienste besitzen Mitarbeiter*in, Beginn und Ende. Der Titel bleibt bei Diensten optional.
- Termine besitzen zusaetzlich einen sprechenden Titel.
- Termine innerhalb eines Dienstes werden diesem Dienst in der Darstellung zugeordnet.
Expand All @@ -45,6 +47,7 @@ Kernprozess:
- Jede angemeldete Person kann im eigenen Einstellungs-Tab Kopano, Google, Apple und einen manuellen CalDAV-Anbieter verbinden. Kopano ist mit `https://mail.adberlin.org` vorbelegt, bleibt aber änderbar. Externe Anbieter erhalten einen sichtbaren, app-eigenen Kalender „AD Dienste“; ihre Kalenderinhalte werden nicht in AD Calendar eingeblendet.
- Persönliche CalDAV-Zugangsdaten und Google-Tokens werden mit Nextclouds Kryptodienst verschlüsselt und als sensible Benutzerkonfiguration gespeichert. Antworten und Logs enthalten weder Passwörter, Tokens, Konto- noch Kalenderkennungen. Google benötigt einen systemweit administrierten OAuth-Webclient; ohne ihn bleibt der persönliche Verbindungsweg deaktiviert.
- Der Google-OAuth-Webclient wird im app-eigenen Nextcloud-Adminabschnitt konfiguriert. Das Client-Secret ist nur schreibbar, wird als sensible lazy AppConfig gespeichert und niemals an Templates oder Statusantworten zurückgegeben; die installationsspezifische Redirect-URI wird dort kopierbar angezeigt.
- Nextcloud-Admins können im app-eigenen Adminabschnitt eine Kopano-CalDAV-Adresse mit temporär eingegebenen Zugangsdaten rein lesend prüfen. Der Test verwendet denselben Kopano-Pfadvertrag wie die persönliche Verbindung, speichert keine Zugangsdaten und legt keinen Kalender an.
- Persönliche Providerverbindungen können parallel bestehen und arbeiten unabhängig vom Opt-out für den internen Nextcloud-Kalender. Ein Providerfehler blockiert weder andere Provider noch die führende AD-Mutation. Der Abgleich bleibt einseitig und überträgt auch extern ausschließlich Dienste.
- Das Kalender-Demo-Pack wird ausschließlich nach ausdrücklicher Bestätigung im app-eigenen Nextcloud-Adminabschnitt installiert; `adcalendar:demo:seed` delegiert auf denselben Service. Es synchronisiert neutrale, benannte Demokonten für jede Kalenderrolle und jeden Bürobereich. Namen tragen die fachliche Demo-Zuordnung in Klammern; Mehrfachrollen werden auch in Gruppentiteln als Hauptrolle mit weiteren Rollen in Klammern dargestellt.
- Demo-Provisioning übernimmt niemals ein vorhandenes fremdes oder LDAP-verwaltetes Konto. Read-only LDAP-Gruppen brechen das Pack im Preflight vor der ersten Mutation ab; eigene lokale Demokonten werden explizit in LocalBase registriert.
Expand Down Expand Up @@ -147,9 +150,9 @@ Urlaubsansichten sind dynamisch ergänzbare Rollen-/Bereichsschnitte. Die Standa

## DDEV

Die gemeinsame Umgebung liegt unter:

~/projects/br-nextcloud-apps/nextcloud-dev
Die gemeinsame Umgebung wird aus dem dokumentierten Parent-Unterverzeichnis
`nextcloud-dev` gesteuert. Bei einem eigenständigen Checkout ist der lokale
DDEV-Pfad zuerst anhand der realen Umgebung zu ermitteln.

Geplante Checks:

Expand All @@ -170,13 +173,9 @@ Geplante Checks:
- Reale Tombstone-/Urlaubsintegration in DDEV: `ddev exec -d /var/www/html/html php custom_apps/adcalendar/tests/integration/DefaultShiftVacationSmoke.php`
- Selbstaufräumender persönlicher DAV-Dienstabgleich in DDEV: `ddev exec -d /var/www/html/html php custom_apps/adcalendar/tests/integration/ShiftCalendarSyncSmoke.php`

## Learnings

### Gemeinsame Suite-Navigation
## Verbindliche Navigation und optionale Integration

- Ohne aktive OrgSuite registriert AD Kalender einen eigenen Nextcloud-Hauptnavigationseintrag. Ab zwei AD-Produkten ersetzt `orgsuite` diesen durch den gemeinsamen Einstieg `AD`.
- Das Template stellt den optionalen Menühost mit `data-suite="ad"` und `data-current-app="adcalendar"` bereit, lädt aber keine OrgSuite-Assets direkt.
- Ohne AD Urlaub bleiben Sperrtermine der manuelle Abwesenheitsweg; fehlende optionale Provider dürfen die Wochenansicht nicht verhindern.
- Fachliche Lese- und Bearbeitungsrechte bleiben ausschliesslich serverseitig im AD Kalender; Menuesichtbarkeit ist keine Berechtigung.

- App-spezifische Kandidaten zielen auf diese Datei; app-uebergreifende Kandidaten werden dem Parent nur als unverbindlicher Vorschlag berichtet. Bewertung und Freigabe folgen dem lokalen Skill `work-in-nextcloud-app`.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## 0.12.0-rc.11

- Kopano-/CalDAV-Fehlerdiagnose mit verständlicher HTTP-405-Meldung im persönlichen Connector und einem rein lesenden administrativen Verbindungstest ergänzt.
- Umschaltbare Zeilen-/Spaltenausrichtung auch in der Monatsansicht sowie fixierte Personenachse beim Scrollen abgesichert.
- Samstage, dunklere Sonntage, gesetzliche Berliner Feiertage und getrennte Markierungen für Heiligabend und Silvester ergänzt, ohne Tagesspalten zu verbreitern.
- Den DAV-Konsistenzjob bei Updates bestehender Installationen idempotent registriert.

## 0.12.0-rc.9

- Google-OAuth-Konfiguration im Nextcloud-Adminabschnitt von AD Kalender ergänzt.
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Wochen- und monatsbasierte Dienst- und Terminplanung mit wiederkehrenden Terminen, Personensuche, Gruppenfiltern, Meetinglückensuche, Standarddienstzeiten, read-only Urlaubsmarkierungen und persönlichem Dienstexport in Nextcloud sowie externe Kalender.

Die Monatsansicht stellt alle betroffenen Kalenderwochen untereinander dar. Tage außerhalb des gewählten Monats sind abgedunkelt; die Personenspalte bleibt beim horizontalen Scrollen sichtbar. Die Auswahl `Woche` oder `Monat` kann zusammen mit den Filtern als persönlicher Standard gespeichert werden.
Die Monatsansicht stellt alle betroffenen Kalenderwochen untereinander dar und lässt sich wie die Wochenansicht zwischen „Tage als Zeilen“ und „Personen als Zeilen“ umschalten. Die zu den Personen gehörende erste Spalte beziehungsweise Kopfzeile bleibt beim Scrollen sichtbar. Tage außerhalb des gewählten Monats sind abgedunkelt; Samstag und Sonntag werden zusätzlich als „Wochenende“ beschriftet. Gesetzliche Feiertage der organisationsweit konfigurierten Kalenderregion werden über den gemeinsamen LocalBase-Kalendervertrag geliefert, namentlich gekennzeichnet und bleiben ohne Auswirkung auf Dienste, Termine oder Rechte. Berlin bleibt Bestandsdefault. Zeitraum und Ausrichtung können zusammen mit den Filtern als persönlicher Standard gespeichert werden.

## Staging-Kompatibilität

Expand All @@ -24,6 +24,8 @@ Der Befehl `adcalendar:demo:seed` ist ausschließlich für synthetische Testdate
Jede angemeldete Person verwaltet Kopano-, Google-, Apple- und manuelle CalDAV-Verbindungen im eigenen Tab `Einstellungen`. AD Calendar erzeugt beim Anbieter einen sichtbaren Kalender `AD Dienste` und exportiert ausschließlich Dienste. Anbieterinhalte werden nicht in AD Calendar eingeblendet oder zurückimportiert.

- Kopano ist mit `https://mail.adberlin.org` vorbelegt; die Adresse bleibt im Verbindungsdialog änderbar.
- Der Kopano-Betreiber muss einen HTTPS-CalDAV-Endpunkt bereitstellen. HTTP 405 wird im Connector ausdrücklich als nicht freigegebener CalDAV-Zugriff erklärt; die notwendige Serverfreigabe kann nicht durch AD Kalender erfolgen.
- Nextcloud-Admins können Adresse und Zugang im AD-Kalender-Adminabschnitt mit einer ausschließlich lesenden CalDAV-Anfrage prüfen. Das Passwort wird weder gespeichert noch zurückgegeben; der Test legt keinen Kalender an.
- Apple und manuelles CalDAV verwenden ein Anbieter- beziehungsweise app-spezifisches Passwort.
- CalDAV-Ziele müssen HTTPS verwenden. Nextclouds HTTP-Client erzwingt zusätzlich seine serverseitige SSRF-Sperre.
- Persönliche Passwörter und Google-Tokens liegen verschlüsselt und als sensible Nextcloud-Benutzerkonfiguration vor.
Expand Down
Loading