chore: complete v1.0 milestone
Archive 5 phases (36 plans) to milestones/v1.0-phases/. Archive roadmap, requirements, and audit to milestones/. Evolve PROJECT.md with shipped state and validated requirements. Collapse ROADMAP.md to one-line milestone summary. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
224
.planning/milestones/v1.0-phases/02-permissions/02-01-PLAN.md
Normal file
224
.planning/milestones/v1.0-phases/02-permissions/02-01-PLAN.md
Normal file
@@ -0,0 +1,224 @@
|
||||
---
|
||||
phase: 02-permissions
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 0
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- SharepointToolbox.Tests/Services/PermissionsServiceTests.cs
|
||||
- SharepointToolbox.Tests/ViewModels/PermissionsViewModelTests.cs
|
||||
- SharepointToolbox.Tests/Services/PermissionEntryClassificationTests.cs
|
||||
- SharepointToolbox.Tests/Services/Export/CsvExportServiceTests.cs
|
||||
- SharepointToolbox.Tests/Services/Export/HtmlExportServiceTests.cs
|
||||
autonomous: true
|
||||
requirements:
|
||||
- PERM-01
|
||||
- PERM-02
|
||||
- PERM-03
|
||||
- PERM-04
|
||||
- PERM-05
|
||||
- PERM-06
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "Running the test suite produces no compilation errors — all test stubs compile against not-yet-existing types using forward-declared interfaces"
|
||||
- "Each test file contains at least one [Fact] method that is marked [Fact(Skip=...)] or calls a stub that returns a known value — no test file is empty"
|
||||
- "dotnet test reports N tests found (not 0) after Wave 0 plans complete"
|
||||
artifacts:
|
||||
- path: "SharepointToolbox.Tests/Services/PermissionsServiceTests.cs"
|
||||
provides: "Test stubs for PERM-01 and PERM-04"
|
||||
- path: "SharepointToolbox.Tests/ViewModels/PermissionsViewModelTests.cs"
|
||||
provides: "Test stubs for PERM-02 multi-site loop"
|
||||
- path: "SharepointToolbox.Tests/Services/PermissionEntryClassificationTests.cs"
|
||||
provides: "Test stubs for PERM-03 external user detection"
|
||||
- path: "SharepointToolbox.Tests/Services/Export/CsvExportServiceTests.cs"
|
||||
provides: "Test stubs for PERM-05 CSV output"
|
||||
- path: "SharepointToolbox.Tests/Services/Export/HtmlExportServiceTests.cs"
|
||||
provides: "Test stubs for PERM-06 HTML output"
|
||||
key_links:
|
||||
- from: "PermissionsServiceTests.cs"
|
||||
to: "IPermissionsService"
|
||||
via: "mock interface"
|
||||
pattern: "IPermissionsService"
|
||||
- from: "PermissionsViewModelTests.cs"
|
||||
to: "IPermissionsService"
|
||||
via: "mock injection"
|
||||
pattern: "IPermissionsService"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Create the Wave 0 test scaffold: all test files needed so that every implementation task in subsequent plans has an automated verify command that references a real test class. Tests are failing stubs (the types they reference do not exist yet), but they must compile once the interfaces and models are defined in Plan 02.
|
||||
|
||||
Purpose: Nyquist compliance — no implementation task is written without a prior test. Tests define the contract, implementation fills it.
|
||||
Output: 5 test files covering PERM-01 through PERM-06 (PERM-07 already covered by Phase 1 SharePointPaginationHelperTests).
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/phases/02-permissions/02-RESEARCH.md
|
||||
@.planning/phases/02-permissions/02-VALIDATION.md
|
||||
|
||||
<interfaces>
|
||||
<!-- Key types from Phase 1 that tests will reference. -->
|
||||
<!-- These are the contracts — executor should use these directly. -->
|
||||
|
||||
From SharepointToolbox/Core/Models/OperationProgress.cs:
|
||||
```csharp
|
||||
namespace SharepointToolbox.Core.Models;
|
||||
public record OperationProgress(int Current, int Total, string Message)
|
||||
{
|
||||
public static OperationProgress Indeterminate(string message) => new(0, 0, message);
|
||||
}
|
||||
```
|
||||
|
||||
Types that WILL EXIST after Plan 02 (write stubs that reference these — they compile once Plan 02 runs):
|
||||
```csharp
|
||||
// SharepointToolbox/Core/Models/PermissionEntry.cs
|
||||
namespace SharepointToolbox.Core.Models;
|
||||
public record PermissionEntry(
|
||||
string ObjectType, string Title, string Url,
|
||||
bool HasUniquePermissions, string Users, string UserLogins,
|
||||
string PermissionLevels, string GrantedThrough, string PrincipalType);
|
||||
|
||||
// SharepointToolbox/Core/Models/ScanOptions.cs
|
||||
namespace SharepointToolbox.Core.Models;
|
||||
public record ScanOptions(
|
||||
bool IncludeInherited = false, bool ScanFolders = true,
|
||||
int FolderDepth = 1, bool IncludeSubsites = false);
|
||||
|
||||
// SharepointToolbox/Services/IPermissionsService.cs
|
||||
namespace SharepointToolbox.Services;
|
||||
public interface IPermissionsService
|
||||
{
|
||||
Task<IReadOnlyList<PermissionEntry>> ScanSiteAsync(
|
||||
Microsoft.SharePoint.Client.ClientContext ctx,
|
||||
ScanOptions options,
|
||||
IProgress<OperationProgress> progress,
|
||||
CancellationToken ct);
|
||||
}
|
||||
|
||||
// SharepointToolbox/Services/Export/CsvExportService.cs
|
||||
namespace SharepointToolbox.Services.Export;
|
||||
public class CsvExportService
|
||||
{
|
||||
public string BuildCsv(IReadOnlyList<PermissionEntry> entries);
|
||||
public Task WriteAsync(IReadOnlyList<PermissionEntry> entries, string filePath, CancellationToken ct);
|
||||
}
|
||||
|
||||
// SharepointToolbox/Services/Export/HtmlExportService.cs
|
||||
namespace SharepointToolbox.Services.Export;
|
||||
public class HtmlExportService
|
||||
{
|
||||
public string BuildHtml(IReadOnlyList<PermissionEntry> entries);
|
||||
public Task WriteAsync(IReadOnlyList<PermissionEntry> entries, string filePath, CancellationToken ct);
|
||||
}
|
||||
```
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 1: Scaffold PermissionsService and ViewModel test stubs</name>
|
||||
<files>
|
||||
SharepointToolbox.Tests/Services/PermissionsServiceTests.cs
|
||||
SharepointToolbox.Tests/ViewModels/PermissionsViewModelTests.cs
|
||||
SharepointToolbox.Tests/Services/PermissionEntryClassificationTests.cs
|
||||
</files>
|
||||
<behavior>
|
||||
PermissionsServiceTests:
|
||||
- Test: ScanSiteAsync_WithIncludeInheritedFalse_SkipsItemsWithoutUniquePermissions — verifies PERM-04 (stub: [Fact(Skip="Requires Plan 02 implementation")])
|
||||
- Test: ScanSiteAsync_ReturnsPermissionEntries_ForMockedSite — verifies PERM-01 (stub)
|
||||
PermissionsViewModelTests:
|
||||
- Test: StartScanAsync_WithMultipleSiteUrls_CallsServiceOncePerUrl — verifies PERM-02 (stub)
|
||||
PermissionEntryClassificationTests:
|
||||
- Test: IsExternalUser_WithExtHashInLoginName_ReturnsTrue — verifies PERM-03 (real test, no stub needed — pure static logic)
|
||||
- Test: IsExternalUser_WithNormalLoginName_ReturnsFalse
|
||||
- Test: PermissionEntry_FiltersOutLimitedAccess_WhenOnlyPermissionIsLimitedAccess
|
||||
</behavior>
|
||||
<action>
|
||||
Create three test files. Each file uses `using SharepointToolbox.Core.Models;` and `using SharepointToolbox.Services;`.
|
||||
|
||||
For PermissionsServiceTests.cs and PermissionsViewModelTests.cs: write stubs with `[Fact(Skip = "Requires live CSOM context — covered by Plan 02 implementation")]`. These compile against `IPermissionsService` which will exist after Plan 02.
|
||||
|
||||
For PermissionEntryClassificationTests.cs: write REAL [Fact] tests that test static helper methods. Define a static helper class `PermissionEntryHelper` in the MAIN project at `SharepointToolbox/Core/Helpers/PermissionEntryHelper.cs` with:
|
||||
- `static bool IsExternalUser(string loginName)` — returns `loginName.Contains("#EXT#", StringComparison.OrdinalIgnoreCase)`
|
||||
- `static IReadOnlyList<string> FilterPermissionLevels(IEnumerable<string> levels)` — removes "Limited Access", returns remaining; returns empty list if all removed
|
||||
- `static bool IsSharingLinksGroup(string loginName)` — returns `loginName.StartsWith("SharingLinks.", StringComparison.OrdinalIgnoreCase) || loginName.Equals("Limited Access System Group", StringComparison.OrdinalIgnoreCase)`
|
||||
|
||||
These are pure functions — tests can run immediately without stubs. Use `Assert.True`, `Assert.False`, `Assert.Empty`, `Assert.Equal`.
|
||||
|
||||
Test file namespace: `SharepointToolbox.Tests.Services` for service tests, `SharepointToolbox.Tests.ViewModels` for VM tests.
|
||||
Also create `SharepointToolbox/Core/Helpers/PermissionEntryHelper.cs` in the main project so the classification tests compile immediately.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>dotnet test C:/Users/dev/Documents/projets/Sharepoint/SharepointToolbox.Tests/SharepointToolbox.Tests.csproj --filter "FullyQualifiedName~PermissionEntryClassificationTests" -x</automated>
|
||||
</verify>
|
||||
<done>PermissionEntryClassificationTests pass (3 tests green). PermissionsServiceTests and PermissionsViewModelTests compile but skip. No new test failures in the existing suite.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 2: Scaffold export service test stubs</name>
|
||||
<files>
|
||||
SharepointToolbox.Tests/Services/Export/CsvExportServiceTests.cs
|
||||
SharepointToolbox.Tests/Services/Export/HtmlExportServiceTests.cs
|
||||
</files>
|
||||
<behavior>
|
||||
CsvExportServiceTests:
|
||||
- Test: BuildCsv_WithKnownEntries_ProducesHeaderRow — verifies CSV has "Object,Title,URL,HasUniquePermissions,Users,UserLogins,Type,Permissions,GrantedThrough" header
|
||||
- Test: BuildCsv_WithDuplicateUserPermissionGrantedThrough_MergesLocations — verifies Merge-PermissionRows behavior: two entries with same Users+PermissionLevels+GrantedThrough but different URLs are merged into one row with URLs pipe-joined
|
||||
- Test: BuildCsv_WithEmptyList_ReturnsHeaderOnly
|
||||
|
||||
HtmlExportServiceTests:
|
||||
- Test: BuildHtml_WithKnownEntries_ContainsUserNames — verifies user names appear in HTML output
|
||||
- Test: BuildHtml_WithEmptyList_ReturnsValidHtml — HTML still renders without entries
|
||||
- Test: BuildHtml_WithExternalUser_ContainsExtHashMarker — verifies external users are distinguishable in HTML
|
||||
|
||||
All tests are REAL [Fact] tests (not stubs) — they will fail until CsvExportService and HtmlExportService are implemented in Plan 03. Write them now so the automated verify in Plan 03 is already defined.
|
||||
</behavior>
|
||||
<action>
|
||||
Create the `SharepointToolbox.Tests/Services/Export/` directory.
|
||||
|
||||
For both test files: reference `SharepointToolbox.Services.Export` namespace and `SharepointToolbox.Core.Models.PermissionEntry`.
|
||||
|
||||
In CsvExportServiceTests.cs: construct sample PermissionEntry instances (hardcoded test data) and call `new CsvExportService().BuildCsv(entries)`. Assert on the resulting string.
|
||||
Sample data for merge test: two entries where Users="alice@contoso.com", PermissionLevels="Contribute", GrantedThrough="Direct Permissions", but with Url="https://contoso.sharepoint.com/sites/A" and "…/sites/B". Merged row must contain "sites/A | sites/B" in URL column.
|
||||
|
||||
In HtmlExportServiceTests.cs: construct a PermissionEntry with Users="Bob Smith", UserLogins="bob@contoso.com", and assert the output HTML contains "Bob Smith". For external user test: UserLogins="ext_user_domain.com#EXT#@contoso.onmicrosoft.com" and assert HTML contains "EXT" or a distinguishing marker.
|
||||
|
||||
These tests will initially FAIL with "type not found" until Plan 03 creates the services. That is expected — they become the automated verify for Plan 03.
|
||||
|
||||
Namespace: `SharepointToolbox.Tests.Services.Export`.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>dotnet test C:/Users/dev/Documents/projets/Sharepoint/SharepointToolbox.Tests/SharepointToolbox.Tests.csproj -x 2>&1 | tail -20</automated>
|
||||
</verify>
|
||||
<done>All existing tests still pass. PermissionEntryClassificationTests (3 tests) pass. CsvExportServiceTests and HtmlExportServiceTests compile but fail with "type not found" — expected until Plan 03. Full test count visible in output.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
After both tasks:
|
||||
- `dotnet test SharepointToolbox.slnx` — existing 44+1 tests still pass (no regressions)
|
||||
- `dotnet test --filter "FullyQualifiedName~PermissionEntryClassificationTests"` — 3 tests green
|
||||
- Test files for PermissionsService, PermissionsViewModel, CsvExport, HtmlExport exist on disk
|
||||
- `PermissionEntryHelper.cs` exists in `SharepointToolbox/Core/Helpers/`
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- PermissionEntryHelper (IsExternalUser, FilterPermissionLevels, IsSharingLinksGroup) implemented and all 3 classification tests pass
|
||||
- 5 test scaffold files exist — each references types in namespaces that Plan 02/03 will create
|
||||
- No existing Phase 1 tests broken
|
||||
- Every subsequent plan's automated verify command points to a test class that exists in one of these 5 files
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/02-permissions/02-01-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user