@ojokesusu/lintasai 1.1.3 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +70 -0
- package/bin/lintasai.js +16 -2
- package/docs/NPX_INSTALL.md +217 -0
- package/kit.ps1 +179 -6
- package/lib/kit-files.psd1 +0 -1
- package/package.json +1 -1
- package/tests/npx-init.Tests.ps1 +237 -0
- package/tests/package-bundle.Tests.ps1 +130 -0
- package/tests/setup-pola-b.Tests.ps1 +288 -0
- package/uninstall.ps1 +25 -3
- package/update-kit.ps1 +15 -2
- package/FIRST_SESSION_PROMPT_v1.md +0 -7
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
#Requires -Module Pester
|
|
2
|
+
|
|
3
|
+
<#
|
|
4
|
+
.SYNOPSIS
|
|
5
|
+
Pester 5+ regression tests untuk v1.1.3 fix - npx-init flow (setup-pola-b.ps1
|
|
6
|
+
via npm wrapper dengan -ProjectRoot eksplisit).
|
|
7
|
+
|
|
8
|
+
.DESCRIPTION
|
|
9
|
+
v1.1.3 fix area:
|
|
10
|
+
1) AGENTS.md.template HARUS ke-bundle di tarball (sebelumnya hilang krn glob).
|
|
11
|
+
2) setup-pola-b.ps1 HARUS terima -ProjectRoot tanpa error (parameter exists).
|
|
12
|
+
3) Saat -ProjectRoot dipakai, $ProjectRoot != Split-Path -Parent $KitDir
|
|
13
|
+
(npm cache path BUKAN root proyek user).
|
|
14
|
+
4) Saat $KitDir bukan child of $ProjectRoot/.claude-kit, script auto-copy
|
|
15
|
+
isi kit dari $PSScriptRoot ke $ProjectRoot/.claude-kit.
|
|
16
|
+
5) Project name di-derive dari leaf $ProjectRoot (bukan parent KitDir).
|
|
17
|
+
6) Setelah init: AGENTS.md ada di $ProjectRoot (bukan di npm cache).
|
|
18
|
+
7) Setelah init: .claude-kit/setup-pola-b.ps1 ada di $ProjectRoot.
|
|
19
|
+
|
|
20
|
+
Strategi:
|
|
21
|
+
- BeforeAll: bikin temp dir mock-npm-cache (simulasi $KitDir) + temp dir
|
|
22
|
+
mock-project-root. Copy kit minimal (atau seluruh kit) ke mock-npm-cache.
|
|
23
|
+
- Jalankan setup-pola-b.ps1 -ProjectRoot <mockProjectRoot> -Force -SkipTeamFiles
|
|
24
|
+
dari mock-npm-cache.
|
|
25
|
+
- Assert hasil: AGENTS.md ada di $mockProjectRoot, .claude-kit/setup-pola-b.ps1
|
|
26
|
+
ada di $mockProjectRoot, dst.
|
|
27
|
+
- AfterAll: cleanup kedua temp dir.
|
|
28
|
+
|
|
29
|
+
Catatan:
|
|
30
|
+
- Test #1 (tarball content) pakai `npm pack --dry-run --json` untuk
|
|
31
|
+
enumerate file list TANPA bikin tarball asli (faster + no disk write).
|
|
32
|
+
- Test #2 (parameter exists) pakai AST static check (tidak invoke script).
|
|
33
|
+
- Test #3-7 perlu actual invocation tapi `-DryRun` tidak cukup karena
|
|
34
|
+
v1.1.3 copy step harus betul-betul dieksekusi untuk verifikasi.
|
|
35
|
+
#>
|
|
36
|
+
|
|
37
|
+
BeforeAll {
|
|
38
|
+
$ErrorActionPreference = 'Stop'
|
|
39
|
+
|
|
40
|
+
# ---- Resolve repo root (kit folder yang berisi setup-pola-b.ps1) ----
|
|
41
|
+
$script:KitRepoRoot = Resolve-Path (Join-Path $PSScriptRoot '..') | Select-Object -ExpandProperty Path
|
|
42
|
+
$script:SetupScript = Join-Path $script:KitRepoRoot 'setup-pola-b.ps1'
|
|
43
|
+
$script:PackageJson = Join-Path $script:KitRepoRoot 'package.json'
|
|
44
|
+
$script:AgentsTemplate = Join-Path $script:KitRepoRoot 'AGENTS.md.template'
|
|
45
|
+
|
|
46
|
+
if (-not (Test-Path $script:SetupScript)) {
|
|
47
|
+
throw "setup-pola-b.ps1 not found at $script:SetupScript"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# ---- Temp dir untuk mock-npm-cache + mock-project-root ----
|
|
51
|
+
$stamp = Get-Date -Format 'yyyyMMddHHmmssfff'
|
|
52
|
+
$script:TempBase = Join-Path $env:TEMP "lintasAI-npx-init-tests-$stamp"
|
|
53
|
+
$script:MockNpmCache = Join-Path $script:TempBase 'npm-cache-kit'
|
|
54
|
+
$script:MockProjectRoot = Join-Path $script:TempBase 'my-shiny-project'
|
|
55
|
+
|
|
56
|
+
New-Item -ItemType Directory -Path $script:MockNpmCache -Force | Out-Null
|
|
57
|
+
New-Item -ItemType Directory -Path $script:MockProjectRoot -Force | Out-Null
|
|
58
|
+
|
|
59
|
+
# ---- Copy seluruh kit ke mock-npm-cache (simulasi npm extract tarball) ----
|
|
60
|
+
# Pakai robocopy supaya cepat, exclude tests/ supaya tidak rekursif.
|
|
61
|
+
$robocopyArgs = @(
|
|
62
|
+
$script:KitRepoRoot,
|
|
63
|
+
$script:MockNpmCache,
|
|
64
|
+
'/E', # include subdirs (empty too)
|
|
65
|
+
'/NFL', '/NDL', '/NJH', '/NJS', '/NC', '/NS', '/NP', # quiet
|
|
66
|
+
'/XD', 'node_modules', '.git'
|
|
67
|
+
)
|
|
68
|
+
& robocopy @robocopyArgs | Out-Null
|
|
69
|
+
# robocopy exit 0-7 = success; 8+ = error
|
|
70
|
+
if ($LASTEXITCODE -ge 8) {
|
|
71
|
+
throw "robocopy failed with exit code $LASTEXITCODE"
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
$script:MockSetupScript = Join-Path $script:MockNpmCache 'setup-pola-b.ps1'
|
|
75
|
+
if (-not (Test-Path $script:MockSetupScript)) {
|
|
76
|
+
throw "Mock kit setup script missing at $script:MockSetupScript - copy failed"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
AfterAll {
|
|
81
|
+
if ($script:TempBase -and (Test-Path $script:TempBase)) {
|
|
82
|
+
Remove-Item -Path $script:TempBase -Recurse -Force -ErrorAction SilentlyContinue
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
Describe "v1.1.3 - npm pack bundles AGENTS.md.template" {
|
|
87
|
+
It "AGENTS.md.template appears in npm pack file list" {
|
|
88
|
+
# `npm pack --dry-run --json` returns metadata including the file list
|
|
89
|
+
# without actually creating a tarball on disk.
|
|
90
|
+
Push-Location $script:KitRepoRoot
|
|
91
|
+
try {
|
|
92
|
+
$packJson = & npm pack --dry-run --json 2>$null | Out-String
|
|
93
|
+
$packJson | Should -Not -BeNullOrEmpty
|
|
94
|
+
$packData = $packJson | ConvertFrom-Json
|
|
95
|
+
# npm returns array of package objects
|
|
96
|
+
$pkg = if ($packData -is [array]) { $packData[0] } else { $packData }
|
|
97
|
+
$files = $pkg.files | ForEach-Object { $_.path }
|
|
98
|
+
$files | Should -Contain 'AGENTS.md.template'
|
|
99
|
+
} finally {
|
|
100
|
+
Pop-Location
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
Describe "v1.1.3 - setup-pola-b.ps1 accepts -ProjectRoot parameter" {
|
|
106
|
+
It "Has -ProjectRoot parameter declared in param block (AST check)" {
|
|
107
|
+
$tokens = $null
|
|
108
|
+
$errors = $null
|
|
109
|
+
$ast = [System.Management.Automation.Language.Parser]::ParseFile(
|
|
110
|
+
$script:SetupScript, [ref]$tokens, [ref]$errors
|
|
111
|
+
)
|
|
112
|
+
$errors | Where-Object { $_.IncompleteInput -eq $false } | Should -BeNullOrEmpty
|
|
113
|
+
|
|
114
|
+
# Find top-level param block
|
|
115
|
+
$paramBlock = $ast.ParamBlock
|
|
116
|
+
$paramBlock | Should -Not -BeNullOrEmpty
|
|
117
|
+
|
|
118
|
+
$paramNames = $paramBlock.Parameters | ForEach-Object { $_.Name.VariablePath.UserPath }
|
|
119
|
+
$paramNames | Should -Contain 'ProjectRoot'
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
It "Invokes without parameter binding error when -ProjectRoot supplied" {
|
|
123
|
+
# Quick sanity: -DryRun -Force -SkipTeamFiles + -ProjectRoot should
|
|
124
|
+
# at minimum get past param binding (may fail later for other reasons
|
|
125
|
+
# but NOT ParameterBindingException).
|
|
126
|
+
$sandboxRoot = Join-Path $script:TempBase 'paramcheck-root'
|
|
127
|
+
New-Item -ItemType Directory -Path $sandboxRoot -Force | Out-Null
|
|
128
|
+
|
|
129
|
+
$output = & powershell.exe -NoProfile -ExecutionPolicy Bypass `
|
|
130
|
+
-File $script:MockSetupScript `
|
|
131
|
+
-ProjectRoot $sandboxRoot `
|
|
132
|
+
-DryRun -Force -SkipTeamFiles 2>&1 | Out-String
|
|
133
|
+
|
|
134
|
+
# ParameterBindingException = test fail
|
|
135
|
+
$output | Should -Not -Match 'ParameterBindingException'
|
|
136
|
+
$output | Should -Not -Match "parameter cannot be found that matches"
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
Describe "v1.1.3 - ProjectRoot resolution (npx wrapper mode)" {
|
|
141
|
+
It "When -ProjectRoot supplied, root = explicit value (NOT parent of KitDir)" {
|
|
142
|
+
# Run setup with -ProjectRoot = mockProjectRoot. KitDir = mockNpmCache.
|
|
143
|
+
# If fix broken: $ProjectRoot would default to Split-Path -Parent $KitDir
|
|
144
|
+
# = $script:TempBase (parent of mockNpmCache), WRONG.
|
|
145
|
+
# If fix works: $ProjectRoot stays at mockProjectRoot.
|
|
146
|
+
# Verify by checking that AGENTS.md lands in mockProjectRoot, not TempBase.
|
|
147
|
+
|
|
148
|
+
# Clean target first to avoid stale state from prior tests
|
|
149
|
+
$targetRoot = Join-Path $script:TempBase 'resolve-check-root'
|
|
150
|
+
if (Test-Path $targetRoot) { Remove-Item -Path $targetRoot -Recurse -Force }
|
|
151
|
+
New-Item -ItemType Directory -Path $targetRoot -Force | Out-Null
|
|
152
|
+
|
|
153
|
+
$output = & powershell.exe -NoProfile -ExecutionPolicy Bypass `
|
|
154
|
+
-File $script:MockSetupScript `
|
|
155
|
+
-ProjectRoot $targetRoot `
|
|
156
|
+
-Force -SkipTeamFiles 2>&1 | Out-String
|
|
157
|
+
|
|
158
|
+
# AGENTS.md must exist in targetRoot (explicit -ProjectRoot honored)
|
|
159
|
+
$agentsInTarget = Join-Path $targetRoot 'AGENTS.md'
|
|
160
|
+
Test-Path $agentsInTarget | Should -BeTrue -Because "AGENTS.md should land in -ProjectRoot ($targetRoot), got output:`n$output"
|
|
161
|
+
|
|
162
|
+
# AGENTS.md must NOT exist in TempBase (parent of MockNpmCache)
|
|
163
|
+
$agentsInParent = Join-Path $script:TempBase 'AGENTS.md'
|
|
164
|
+
Test-Path $agentsInParent | Should -BeFalse -Because "AGENTS.md must NOT leak to parent of KitDir"
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
Describe "v1.1.3 - Auto-copy kit when ProjectRoot lacks .claude-kit" {
|
|
169
|
+
It "Triggers copy from `$PSScriptRoot when target .claude-kit absent" {
|
|
170
|
+
# Setup: fresh project root WITHOUT .claude-kit. Run setup with
|
|
171
|
+
# -ProjectRoot pointing there. Script should detect npx mode and copy
|
|
172
|
+
# kit contents from $KitDir (mockNpmCache) to $ProjectRoot/.claude-kit.
|
|
173
|
+
|
|
174
|
+
$autoCopyRoot = Join-Path $script:TempBase 'autocopy-root'
|
|
175
|
+
if (Test-Path $autoCopyRoot) { Remove-Item -Path $autoCopyRoot -Recurse -Force }
|
|
176
|
+
New-Item -ItemType Directory -Path $autoCopyRoot -Force | Out-Null
|
|
177
|
+
|
|
178
|
+
# Pre-condition: no .claude-kit yet
|
|
179
|
+
$kitTargetDir = Join-Path $autoCopyRoot '.claude-kit'
|
|
180
|
+
Test-Path $kitTargetDir | Should -BeFalse
|
|
181
|
+
|
|
182
|
+
$output = & powershell.exe -NoProfile -ExecutionPolicy Bypass `
|
|
183
|
+
-File $script:MockSetupScript `
|
|
184
|
+
-ProjectRoot $autoCopyRoot `
|
|
185
|
+
-Force -SkipTeamFiles 2>&1 | Out-String
|
|
186
|
+
|
|
187
|
+
# Post-condition: .claude-kit created with key files
|
|
188
|
+
Test-Path $kitTargetDir | Should -BeTrue -Because "Script should auto-copy kit to .claude-kit, output:`n$output"
|
|
189
|
+
Test-Path (Join-Path $kitTargetDir 'setup-pola-b.ps1') | Should -BeTrue
|
|
190
|
+
Test-Path (Join-Path $kitTargetDir 'CHANGELOG.md') | Should -BeTrue
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
Describe "v1.1.3 - Project name derived from -ProjectRoot leaf" {
|
|
195
|
+
It "Project name = Split-Path -Leaf `$ProjectRoot (not KitDir parent)" {
|
|
196
|
+
$namedRoot = Join-Path $script:TempBase 'unique-project-name-xyz'
|
|
197
|
+
if (Test-Path $namedRoot) { Remove-Item -Path $namedRoot -Recurse -Force }
|
|
198
|
+
New-Item -ItemType Directory -Path $namedRoot -Force | Out-Null
|
|
199
|
+
|
|
200
|
+
$output = & powershell.exe -NoProfile -ExecutionPolicy Bypass `
|
|
201
|
+
-File $script:MockSetupScript `
|
|
202
|
+
-ProjectRoot $namedRoot `
|
|
203
|
+
-Force -SkipTeamFiles 2>&1 | Out-String
|
|
204
|
+
|
|
205
|
+
$agentsPath = Join-Path $namedRoot 'AGENTS.md'
|
|
206
|
+
Test-Path $agentsPath | Should -BeTrue -Because "AGENTS.md must exist before name check, output:`n$output"
|
|
207
|
+
|
|
208
|
+
# Project name appears in deployed AGENTS.md (template fills it in heading)
|
|
209
|
+
$agentsContent = Get-Content -Path $agentsPath -Raw
|
|
210
|
+
$expectedName = Split-Path -Leaf $namedRoot
|
|
211
|
+
$agentsContent | Should -Match ([regex]::Escape($expectedName)) -Because "AGENTS.md should reference project name '$expectedName' from -ProjectRoot leaf"
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
Describe "v1.1.3 - Post-init artifacts in `$ProjectRoot" {
|
|
216
|
+
BeforeAll {
|
|
217
|
+
# One init run shared by both `It` blocks below
|
|
218
|
+
$script:PostInitRoot = Join-Path $script:TempBase 'postinit-root'
|
|
219
|
+
if (Test-Path $script:PostInitRoot) { Remove-Item -Path $script:PostInitRoot -Recurse -Force }
|
|
220
|
+
New-Item -ItemType Directory -Path $script:PostInitRoot -Force | Out-Null
|
|
221
|
+
|
|
222
|
+
$script:PostInitOutput = & powershell.exe -NoProfile -ExecutionPolicy Bypass `
|
|
223
|
+
-File $script:MockSetupScript `
|
|
224
|
+
-ProjectRoot $script:PostInitRoot `
|
|
225
|
+
-Force -SkipTeamFiles 2>&1 | Out-String
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
It "AGENTS.md exists at `$ProjectRoot root" {
|
|
229
|
+
$agentsPath = Join-Path $script:PostInitRoot 'AGENTS.md'
|
|
230
|
+
Test-Path $agentsPath | Should -BeTrue -Because "Post-init AGENTS.md must exist, output:`n$script:PostInitOutput"
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
It ".claude-kit/setup-pola-b.ps1 exists at `$ProjectRoot/.claude-kit" {
|
|
234
|
+
$kitSetup = Join-Path $script:PostInitRoot '.claude-kit\setup-pola-b.ps1'
|
|
235
|
+
Test-Path $kitSetup | Should -BeTrue -Because "Post-init kit setup script must exist in deployed .claude-kit"
|
|
236
|
+
}
|
|
237
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#Requires -Module Pester
|
|
2
|
+
|
|
3
|
+
<#
|
|
4
|
+
.SYNOPSIS
|
|
5
|
+
Regression tests untuk package.json files[] bundle.
|
|
6
|
+
|
|
7
|
+
.DESCRIPTION
|
|
8
|
+
Memastikan `npm pack` mem-bundle SEMUA file kritikal kit
|
|
9
|
+
(AGENTS.md.template, bin/lintasai.js, lib/, templates/, docs/, tests/).
|
|
10
|
+
|
|
11
|
+
Latar belakang: pernah ada regression di mana AGENTS.md.template
|
|
12
|
+
hilang dari tarball karena pattern files[] tidak match.
|
|
13
|
+
Test ini = guard supaya regression yang sama tidak terulang.
|
|
14
|
+
|
|
15
|
+
.NOTES
|
|
16
|
+
Pakai `npm pack --dry-run --json` untuk parsing deterministic.
|
|
17
|
+
#>
|
|
18
|
+
|
|
19
|
+
BeforeAll {
|
|
20
|
+
$script:KitRoot = Resolve-Path (Join-Path $PSScriptRoot '..')
|
|
21
|
+
$script:PackageJsonPath = Join-Path $script:KitRoot 'package.json'
|
|
22
|
+
|
|
23
|
+
# Run `npm pack --dry-run --json` SEKALI di BeforeAll (mahal).
|
|
24
|
+
Push-Location $script:KitRoot
|
|
25
|
+
try {
|
|
26
|
+
$rawOutput = & npm pack --dry-run --json 2>&1
|
|
27
|
+
$exitCode = $LASTEXITCODE
|
|
28
|
+
} finally {
|
|
29
|
+
Pop-Location
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if ($exitCode -ne 0) {
|
|
33
|
+
throw "npm pack --dry-run gagal (exit $exitCode). Output: $rawOutput"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# npm pack --json kadang campur stderr ke stdout; ambil JSON array/object di akhir.
|
|
37
|
+
$jsonText = ($rawOutput | Out-String).Trim()
|
|
38
|
+
# Cari posisi '[' atau '{' pertama yang masuk akal — npm pack --json balikin array.
|
|
39
|
+
$jsonStart = $jsonText.IndexOf('[')
|
|
40
|
+
if ($jsonStart -lt 0) { $jsonStart = $jsonText.IndexOf('{') }
|
|
41
|
+
if ($jsonStart -gt 0) {
|
|
42
|
+
$jsonText = $jsonText.Substring($jsonStart)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
$script:PackInfo = $jsonText | ConvertFrom-Json
|
|
46
|
+
# npm pack --json returns array; ambil entry pertama.
|
|
47
|
+
if ($script:PackInfo -is [array]) {
|
|
48
|
+
$script:PackInfo = $script:PackInfo[0]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# File list (relative path di dalam tarball).
|
|
52
|
+
$script:PackFiles = @($script:PackInfo.files | ForEach-Object { $_.path })
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
Describe "package.json structural integrity" {
|
|
56
|
+
It "package.json adalah valid JSON" {
|
|
57
|
+
{ Get-Content -Raw -Path $script:PackageJsonPath | ConvertFrom-Json } | Should -Not -Throw
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
It "package.json punya files[] array" {
|
|
61
|
+
$pkg = Get-Content -Raw -Path $script:PackageJsonPath | ConvertFrom-Json
|
|
62
|
+
$pkg.files | Should -Not -BeNullOrEmpty
|
|
63
|
+
$pkg.files.Count | Should -BeGreaterThan 0
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
It "files[] include pattern untuk AGENTS.md.template (explicit atau via wildcard)" {
|
|
67
|
+
$pkg = Get-Content -Raw -Path $script:PackageJsonPath | ConvertFrom-Json
|
|
68
|
+
$patterns = @($pkg.files)
|
|
69
|
+
|
|
70
|
+
# Match jika ada salah satu: explicit "AGENTS.md.template", wildcard "*.template", atau wildcard "*.md".
|
|
71
|
+
$hasExplicit = $patterns -contains 'AGENTS.md.template'
|
|
72
|
+
$hasTemplateWildcard = $patterns -contains '*.template'
|
|
73
|
+
$hasMdWildcard = $patterns -contains '*.md'
|
|
74
|
+
|
|
75
|
+
($hasExplicit -or $hasTemplateWildcard -or $hasMdWildcard) | Should -BeTrue `
|
|
76
|
+
-Because "AGENTS.md.template harus ter-cover oleh pattern di files[] (explicit atau wildcard)"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
Describe "npm pack --dry-run bundle coverage" {
|
|
81
|
+
It "Tarball memuat AGENTS.md.template" {
|
|
82
|
+
$script:PackFiles | Should -Contain 'AGENTS.md.template' `
|
|
83
|
+
-Because "AGENTS.md.template adalah file kritikal kit; tanpa ini setup Pola B di project gagal"
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
It "Tarball memuat bin/lintasai.js" {
|
|
87
|
+
$script:PackFiles | Should -Contain 'bin/lintasai.js' `
|
|
88
|
+
-Because "Entry point CLI; tanpa ini `npx lintasai` tidak jalan"
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
It "Tarball memuat file di lib/" {
|
|
92
|
+
$libFiles = @($script:PackFiles | Where-Object { $_ -like 'lib/*' })
|
|
93
|
+
$libFiles.Count | Should -BeGreaterThan 0 -Because "lib/ harus ada di tarball"
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
It "Tarball memuat file di templates/" {
|
|
97
|
+
$templateFiles = @($script:PackFiles | Where-Object { $_ -like 'templates/*' })
|
|
98
|
+
$templateFiles.Count | Should -BeGreaterThan 0 -Because "templates/ harus ada di tarball"
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
It "Tarball memuat file di docs/" {
|
|
102
|
+
$docsFiles = @($script:PackFiles | Where-Object { $_ -like 'docs/*' })
|
|
103
|
+
$docsFiles.Count | Should -BeGreaterThan 0 -Because "docs/ harus ada di tarball"
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
It "Tarball memuat file di tests/" {
|
|
107
|
+
$testFiles = @($script:PackFiles | Where-Object { $_ -like 'tests/*' })
|
|
108
|
+
$testFiles.Count | Should -BeGreaterThan 0 -Because "tests/ harus ada di tarball (untuk konsumen yang mau re-verify)"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
Describe "Tarball size & file count sanity" {
|
|
113
|
+
It "Tarball size < 2 MB" {
|
|
114
|
+
# npm pack --json balikin `size` (unpacked) dan `unpackedSize`. Pakai size (tarball compressed) kalau ada.
|
|
115
|
+
$size = if ($null -ne $script:PackInfo.size) { $script:PackInfo.size } else { $script:PackInfo.unpackedSize }
|
|
116
|
+
$size | Should -Not -BeNullOrEmpty
|
|
117
|
+
$size | Should -BeLessThan (2 * 1024 * 1024) `
|
|
118
|
+
-Because "Kit harus stay lightweight (<2MB). Saat ini: $([math]::Round($size/1024,1)) KB"
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
It "Tarball entryCount > 50 (sanity check, bukan empty bundle)" {
|
|
122
|
+
$entryCount = if ($null -ne $script:PackInfo.entryCount) {
|
|
123
|
+
$script:PackInfo.entryCount
|
|
124
|
+
} else {
|
|
125
|
+
$script:PackFiles.Count
|
|
126
|
+
}
|
|
127
|
+
$entryCount | Should -BeGreaterThan 50 `
|
|
128
|
+
-Because "Kit punya banyak file (templates, docs, lib, tests). <50 = something missing. Actual: $entryCount"
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
#Requires -Module Pester
|
|
2
|
+
|
|
3
|
+
<#
|
|
4
|
+
.SYNOPSIS
|
|
5
|
+
Pester 5+ tests untuk setup-pola-b.ps1 (ProjectRoot resolution + npx mode copy).
|
|
6
|
+
|
|
7
|
+
.DESCRIPTION
|
|
8
|
+
Empat invocation branches yang di-test:
|
|
9
|
+
1. No -ProjectRoot, kit at <tmp>/.claude-kit/setup-pola-b.ps1
|
|
10
|
+
-> project = parent of kit (traditional Pola B mode).
|
|
11
|
+
2. -ProjectRoot supplied (valid path)
|
|
12
|
+
-> project = that explicit path (no derivation).
|
|
13
|
+
3. -ProjectRoot non-existent path
|
|
14
|
+
-> script errors out clearly (not silent fail). Resolve-Path -ErrorAction Stop
|
|
15
|
+
melempar terminating error; test assert exit code non-zero.
|
|
16
|
+
4. -ProjectRoot supplied + kit at npm-cache-like path (TEMP\nodemod-test\@ojokesusu\lintasai)
|
|
17
|
+
-> npx mode auto-detect, kit di-COPY ke $ProjectRoot/.claude-kit/.
|
|
18
|
+
|
|
19
|
+
Plus:
|
|
20
|
+
5. Manifest sanity: setelah setup sukses, .install-manifest.json terbuat.
|
|
21
|
+
|
|
22
|
+
Strategi:
|
|
23
|
+
- Real kit (parent dari folder tests/) jadi source-of-truth file.
|
|
24
|
+
- Per-test fake project root di $env:TEMP dengan layout sesuai branch.
|
|
25
|
+
- Branch 1, 2, 4, 5: pakai -Force -SkipTeamFiles untuk skip prompt + faster run.
|
|
26
|
+
- Branch 3: pakai path yang sengaja tidak ada, assert exit != 0 + pesan error.
|
|
27
|
+
- Test memanggil script di child PowerShell -NoProfile -NonInteractive supaya
|
|
28
|
+
env caller tidak ke-pollute (Set-StrictMode di lib/manifest.ps1 dll).
|
|
29
|
+
#>
|
|
30
|
+
|
|
31
|
+
BeforeAll {
|
|
32
|
+
# ---- Resolve repo root (kit folder asli, parent dari tests/) ----
|
|
33
|
+
$script:KitRepoRoot = Resolve-Path (Join-Path $PSScriptRoot '..') | Select-Object -ExpandProperty Path
|
|
34
|
+
$script:SetupScript = Join-Path $script:KitRepoRoot 'setup-pola-b.ps1'
|
|
35
|
+
|
|
36
|
+
# ---- Canonical TEMP (long form) ----
|
|
37
|
+
# $env:TEMP sering return 8.3 short path (mis. C:\Users\ADMINI~1\AppData\Local\Temp\2).
|
|
38
|
+
# Setup script pakai $PSScriptRoot (long form) untuk derive ProjectRoot di traditional
|
|
39
|
+
# mode, dan Resolve-Path tidak expand 8.3 -> long. Akibatnya: output script pakai long
|
|
40
|
+
# form, regex test pakai short form -> tidak match. Pakai (Get-Item).FullName supaya
|
|
41
|
+
# canonical long form di semua test path.
|
|
42
|
+
$script:TempCanonical = (Get-Item -LiteralPath $env:TEMP).FullName
|
|
43
|
+
|
|
44
|
+
if (-not (Test-Path $script:SetupScript)) {
|
|
45
|
+
throw "setup-pola-b.ps1 not found at $script:SetupScript - tests assume layout tests/ sibling to setup-pola-b.ps1"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# ---- Helper: copy kit asli ke target folder (jadi .claude-kit di fake project) ----
|
|
49
|
+
function script:Copy-RealKit {
|
|
50
|
+
param(
|
|
51
|
+
[Parameter(Mandatory)][string]$Destination
|
|
52
|
+
)
|
|
53
|
+
if (-not (Test-Path -LiteralPath $Destination)) {
|
|
54
|
+
$null = New-Item -ItemType Directory -Path $Destination -Force
|
|
55
|
+
}
|
|
56
|
+
# Copy semua isi kit kecuali subfolder yang bisa bikin loop/lambat (kalau ada)
|
|
57
|
+
Get-ChildItem -Path $script:KitRepoRoot -Force | Where-Object {
|
|
58
|
+
$_.Name -notin @('.git', 'node_modules')
|
|
59
|
+
} | ForEach-Object {
|
|
60
|
+
$dest = Join-Path $Destination $_.Name
|
|
61
|
+
if ($_.PSIsContainer) {
|
|
62
|
+
Copy-Item -LiteralPath $_.FullName -Destination $dest -Recurse -Force
|
|
63
|
+
} else {
|
|
64
|
+
Copy-Item -LiteralPath $_.FullName -Destination $dest -Force
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# ---- Helper: bikin file dummy supaya proyek "tidak hampir kosong" ----
|
|
70
|
+
# setup-pola-b.ps1 skip docs/ skeleton kalau project root hampir kosong (heuristic
|
|
71
|
+
# menghitung non-hidden file/dir). Test branch 1/2/4/5 mau real flow, jadi taruh
|
|
72
|
+
# 2 file dummy supaya nonHiddenFiles.Count > 1.
|
|
73
|
+
function script:Add-ProjectContent {
|
|
74
|
+
param([Parameter(Mandatory)][string]$Root)
|
|
75
|
+
[System.IO.File]::WriteAllText(
|
|
76
|
+
(Join-Path $Root 'README.md'),
|
|
77
|
+
"# Test Project`nDummy content untuk lewatin proyek-hampir-kosong heuristic.`n",
|
|
78
|
+
(New-Object System.Text.UTF8Encoding $false)
|
|
79
|
+
)
|
|
80
|
+
[System.IO.File]::WriteAllText(
|
|
81
|
+
(Join-Path $Root 'package.json'),
|
|
82
|
+
'{"name":"test","version":"0.0.1"}',
|
|
83
|
+
(New-Object System.Text.UTF8Encoding $false)
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# ---- Helper: run setup-pola-b.ps1 di child PowerShell, capture stdout + exit ----
|
|
88
|
+
function script:Invoke-Setup {
|
|
89
|
+
param(
|
|
90
|
+
[Parameter(Mandatory)][string]$ScriptPath,
|
|
91
|
+
[string[]]$Args = @()
|
|
92
|
+
)
|
|
93
|
+
$pwshExe = (Get-Process -Id $PID).Path
|
|
94
|
+
if (-not $pwshExe) { $pwshExe = 'powershell.exe' }
|
|
95
|
+
$allArgs = @('-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File', $ScriptPath) + $Args
|
|
96
|
+
$output = & $pwshExe @allArgs 2>&1 | Out-String
|
|
97
|
+
return [pscustomobject]@{
|
|
98
|
+
Output = $output
|
|
99
|
+
ExitCode = $LASTEXITCODE
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# ---- Helper: cleanup folder kalau ada ----
|
|
104
|
+
function script:Remove-TestRoot {
|
|
105
|
+
param([Parameter(Mandatory)][string]$Path)
|
|
106
|
+
if (Test-Path -LiteralPath $Path) {
|
|
107
|
+
Remove-Item -Recurse -Force -LiteralPath $Path -ErrorAction SilentlyContinue
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
Describe "setup-pola-b.ps1 Branch 1: traditional invocation (no -ProjectRoot)" {
|
|
113
|
+
BeforeAll {
|
|
114
|
+
# Layout: <tmp>/<project>/.claude-kit/setup-pola-b.ps1
|
|
115
|
+
$script:Root1 = Join-Path $script:TempCanonical ("lintasAI-setup-test-traditional-{0}" -f ([guid]::NewGuid().ToString('N').Substring(0,8)))
|
|
116
|
+
$null = New-Item -ItemType Directory -Path $script:Root1 -Force
|
|
117
|
+
script:Add-ProjectContent -Root $script:Root1
|
|
118
|
+
$script:Kit1 = Join-Path $script:Root1 '.claude-kit'
|
|
119
|
+
script:Copy-RealKit -Destination $script:Kit1
|
|
120
|
+
$script:SetupInKit1 = Join-Path $script:Kit1 'setup-pola-b.ps1'
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
AfterAll {
|
|
124
|
+
script:Remove-TestRoot -Path $script:Root1
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
It "Resolves project root = parent of kit folder" {
|
|
128
|
+
$result = script:Invoke-Setup -ScriptPath $script:SetupInKit1 -Args @('-Force', '-SkipTeamFiles')
|
|
129
|
+
$result.ExitCode | Should -Be 0
|
|
130
|
+
# Output harus mention root proyek sesuai $Root1.
|
|
131
|
+
$result.Output | Should -Match ([regex]::Escape($script:Root1))
|
|
132
|
+
# AGENTS.md harus dideploy di project root.
|
|
133
|
+
(Test-Path -LiteralPath (Join-Path $script:Root1 'AGENTS.md')) | Should -BeTrue
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
It "Does NOT print npx mode banner" {
|
|
137
|
+
# Traditional mode: tidak boleh ada string "[npx] Mode" karena ProjectRoot tidak di-pass.
|
|
138
|
+
# Re-run dengan -DryRun supaya tidak duplicate setup di same root.
|
|
139
|
+
$result = script:Invoke-Setup -ScriptPath $script:SetupInKit1 -Args @('-Force', '-SkipTeamFiles', '-DryRun')
|
|
140
|
+
$result.Output | Should -Not -Match '\[npx\] Mode'
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
Describe "setup-pola-b.ps1 Branch 2: -ProjectRoot supplied (valid path)" {
|
|
145
|
+
BeforeAll {
|
|
146
|
+
# Layout: <tmp>/<project>/.claude-kit/setup-pola-b.ps1 (kit lives standard place)
|
|
147
|
+
# but we ALSO pass -ProjectRoot explicitly to exercise the npx-mode code path
|
|
148
|
+
# bahkan kalau path-nya same. Script harus treat sebagai npx mode.
|
|
149
|
+
$script:Root2 = Join-Path $script:TempCanonical ("lintasAI-setup-test-explicitroot-{0}" -f ([guid]::NewGuid().ToString('N').Substring(0,8)))
|
|
150
|
+
$null = New-Item -ItemType Directory -Path $script:Root2 -Force
|
|
151
|
+
script:Add-ProjectContent -Root $script:Root2
|
|
152
|
+
$script:Kit2 = Join-Path $script:Root2 '.claude-kit'
|
|
153
|
+
script:Copy-RealKit -Destination $script:Kit2
|
|
154
|
+
$script:SetupInKit2 = Join-Path $script:Kit2 'setup-pola-b.ps1'
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
AfterAll {
|
|
158
|
+
script:Remove-TestRoot -Path $script:Root2
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
It "Uses the explicit ProjectRoot path (no derivation)" {
|
|
162
|
+
$result = script:Invoke-Setup -ScriptPath $script:SetupInKit2 -Args @(
|
|
163
|
+
'-Force', '-SkipTeamFiles', '-ProjectRoot', $script:Root2
|
|
164
|
+
)
|
|
165
|
+
$result.ExitCode | Should -Be 0
|
|
166
|
+
# Banner npx mode harus muncul (proves the branch executed).
|
|
167
|
+
$result.Output | Should -Match '\[npx\] Mode: explicit ProjectRoot'
|
|
168
|
+
# AGENTS.md harus ada di explicit root.
|
|
169
|
+
(Test-Path -LiteralPath (Join-Path $script:Root2 'AGENTS.md')) | Should -BeTrue
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
Describe "setup-pola-b.ps1 Branch 3: -ProjectRoot non-existent path" {
|
|
174
|
+
BeforeAll {
|
|
175
|
+
# Kit lives di TEMP/<kit-folder>/.claude-kit dengan parent project legit, tapi
|
|
176
|
+
# we pass -ProjectRoot ke path yang tidak ada -> Resolve-Path -ErrorAction Stop
|
|
177
|
+
# harus throw -> exit code non-zero, with clear error message (not silent).
|
|
178
|
+
$script:Root3 = Join-Path $script:TempCanonical ("lintasAI-setup-test-badroot-{0}" -f ([guid]::NewGuid().ToString('N').Substring(0,8)))
|
|
179
|
+
$null = New-Item -ItemType Directory -Path $script:Root3 -Force
|
|
180
|
+
script:Add-ProjectContent -Root $script:Root3
|
|
181
|
+
$script:Kit3 = Join-Path $script:Root3 '.claude-kit'
|
|
182
|
+
script:Copy-RealKit -Destination $script:Kit3
|
|
183
|
+
$script:SetupInKit3 = Join-Path $script:Kit3 'setup-pola-b.ps1'
|
|
184
|
+
# Path yang DIJAMIN tidak ada
|
|
185
|
+
$script:BadRoot = Join-Path $script:TempCanonical ("lintasAI-DOES-NOT-EXIST-{0}" -f ([guid]::NewGuid().ToString('N').Substring(0,8)))
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
AfterAll {
|
|
189
|
+
script:Remove-TestRoot -Path $script:Root3
|
|
190
|
+
script:Remove-TestRoot -Path $script:BadRoot # defensive (script harusnya tidak create)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
It "Errors out (non-zero exit) with clear message" {
|
|
194
|
+
$result = script:Invoke-Setup -ScriptPath $script:SetupInKit3 -Args @(
|
|
195
|
+
'-Force', '-SkipTeamFiles', '-ProjectRoot', $script:BadRoot
|
|
196
|
+
)
|
|
197
|
+
$result.ExitCode | Should -Not -Be 0
|
|
198
|
+
# Pesan harus include "Resolve-Path" / "not exist" / nama path yang tidak valid -
|
|
199
|
+
# apa pun yang membuktikan ini BUKAN silent fail.
|
|
200
|
+
# Resolve-Path throws "Cannot find path '...' because it does not exist."
|
|
201
|
+
$result.Output | Should -Match '(?i)(cannot find path|does not exist|not exist|tidak ditemukan|tidak ada)'
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
It "Does NOT create .claude-kit at the bogus path" {
|
|
205
|
+
# Script tidak boleh men-create folder di path yang tidak ada (defensive guard).
|
|
206
|
+
(Test-Path -LiteralPath $script:BadRoot) | Should -BeFalse
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
Describe "setup-pola-b.ps1 Branch 4: -ProjectRoot + kit at npm-cache-like path" {
|
|
211
|
+
BeforeAll {
|
|
212
|
+
# Layout simulasi npx:
|
|
213
|
+
# <tmp>\nodemod-test\@ojokesusu\lintasai\setup-pola-b.ps1 (kit di npm cache)
|
|
214
|
+
# <tmp>\<project-root>\ (target project, beda parent)
|
|
215
|
+
# Script harus detect mismatch ($KitDir tidak dalam $ProjectRoot/.claude-kit/)
|
|
216
|
+
# dan COPY isi kit ke $ProjectRoot/.claude-kit/.
|
|
217
|
+
$script:Root4 = Join-Path $script:TempCanonical ("lintasAI-setup-test-npx-{0}" -f ([guid]::NewGuid().ToString('N').Substring(0,8)))
|
|
218
|
+
$null = New-Item -ItemType Directory -Path $script:Root4 -Force
|
|
219
|
+
script:Add-ProjectContent -Root $script:Root4
|
|
220
|
+
|
|
221
|
+
# Kit DIPASANG di npm-cache-like location (BUKAN di $script:Root4/.claude-kit)
|
|
222
|
+
$script:NpmCacheRoot = Join-Path $script:TempCanonical ("nodemod-test-{0}" -f ([guid]::NewGuid().ToString('N').Substring(0,8)))
|
|
223
|
+
$script:KitInNpmCache = Join-Path $script:NpmCacheRoot '@ojokesusu\lintasai'
|
|
224
|
+
$null = New-Item -ItemType Directory -Path $script:KitInNpmCache -Force
|
|
225
|
+
script:Copy-RealKit -Destination $script:KitInNpmCache
|
|
226
|
+
$script:SetupInNpmCache = Join-Path $script:KitInNpmCache 'setup-pola-b.ps1'
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
AfterAll {
|
|
230
|
+
script:Remove-TestRoot -Path $script:Root4
|
|
231
|
+
script:Remove-TestRoot -Path $script:NpmCacheRoot
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
It "Detects npx mode and copies kit to <ProjectRoot>/.claude-kit/" {
|
|
235
|
+
# Pre-assert: target .claude-kit tidak ada sebelum jalan.
|
|
236
|
+
$targetKit = Join-Path $script:Root4 '.claude-kit'
|
|
237
|
+
(Test-Path -LiteralPath $targetKit) | Should -BeFalse
|
|
238
|
+
|
|
239
|
+
$result = script:Invoke-Setup -ScriptPath $script:SetupInNpmCache -Args @(
|
|
240
|
+
'-Force', '-SkipTeamFiles', '-ProjectRoot', $script:Root4
|
|
241
|
+
)
|
|
242
|
+
$result.ExitCode | Should -Be 0
|
|
243
|
+
# Verifikasi log npx mode + copy
|
|
244
|
+
$result.Output | Should -Match '\[npx\] Mode: explicit ProjectRoot'
|
|
245
|
+
$result.Output | Should -Match '(?i)\[npx\] Copy kit'
|
|
246
|
+
|
|
247
|
+
# Hasil: .claude-kit di project root harus ada, berisi setup-pola-b.ps1 yang ke-copy
|
|
248
|
+
(Test-Path -LiteralPath $targetKit) | Should -BeTrue
|
|
249
|
+
(Test-Path -LiteralPath (Join-Path $targetKit 'setup-pola-b.ps1')) | Should -BeTrue
|
|
250
|
+
(Test-Path -LiteralPath (Join-Path $targetKit 'CHANGELOG.md')) | Should -BeTrue
|
|
251
|
+
(Test-Path -LiteralPath (Join-Path $targetKit 'lib\manifest.ps1')) | Should -BeTrue
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
Describe "setup-pola-b.ps1 manifest creation" {
|
|
256
|
+
BeforeAll {
|
|
257
|
+
# Reuse pattern Branch 1 (traditional) – setelah setup sukses, manifest harus
|
|
258
|
+
# terbuat di .claude-kit/.install-manifest.json. Pakai fresh root supaya hash
|
|
259
|
+
# & state-nya independent dari describe lain.
|
|
260
|
+
$script:Root5 = Join-Path $script:TempCanonical ("lintasAI-setup-test-manifest-{0}" -f ([guid]::NewGuid().ToString('N').Substring(0,8)))
|
|
261
|
+
$null = New-Item -ItemType Directory -Path $script:Root5 -Force
|
|
262
|
+
script:Add-ProjectContent -Root $script:Root5
|
|
263
|
+
$script:Kit5 = Join-Path $script:Root5 '.claude-kit'
|
|
264
|
+
script:Copy-RealKit -Destination $script:Kit5
|
|
265
|
+
$script:SetupInKit5 = Join-Path $script:Kit5 'setup-pola-b.ps1'
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
AfterAll {
|
|
269
|
+
script:Remove-TestRoot -Path $script:Root5
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
It "Creates .install-manifest.json after successful setup" {
|
|
273
|
+
$result = script:Invoke-Setup -ScriptPath $script:SetupInKit5 -Args @('-Force', '-SkipTeamFiles')
|
|
274
|
+
$result.ExitCode | Should -Be 0
|
|
275
|
+
$manifestPath = Join-Path $script:Kit5 '.install-manifest.json'
|
|
276
|
+
(Test-Path -LiteralPath $manifestPath) | Should -BeTrue
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
It "Manifest is valid JSON with expected top-level fields" {
|
|
280
|
+
$manifestPath = Join-Path $script:Kit5 '.install-manifest.json'
|
|
281
|
+
(Test-Path -LiteralPath $manifestPath) | Should -BeTrue
|
|
282
|
+
$json = Get-Content -LiteralPath $manifestPath -Raw -Encoding UTF8
|
|
283
|
+
{ $json | ConvertFrom-Json -ErrorAction Stop } | Should -Not -Throw
|
|
284
|
+
$obj = $json | ConvertFrom-Json
|
|
285
|
+
$obj.PSObject.Properties.Name | Should -Contain 'schema_version'
|
|
286
|
+
$obj.PSObject.Properties.Name | Should -Contain 'files'
|
|
287
|
+
}
|
|
288
|
+
}
|