appydave-tools 0.66.0 → 0.68.0

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.
@@ -0,0 +1,374 @@
1
+ # User Acceptance Test Plan - DAM CLI Enhancements
2
+
3
+ **Test Date:** 2025-01-22
4
+ **Commit Range:** `9e49668` → `4228b51` (75 commits)
5
+ **Test Scope:** Verify core functionality intact + new features working
6
+
7
+ ---
8
+
9
+ ## Delivery Method
10
+
11
+ This test plan will be executed **interactively** using the following process:
12
+
13
+ 1. **AI announces:** "Testing [feature]: [description]"
14
+ 2. **AI runs:** `command here`
15
+ 3. **AI shows:** Output from command
16
+ 4. **User responds:**
17
+ - Type `1` to proceed to next test (affirmative)
18
+ - OR provide feedback/change of plan
19
+
20
+ **Important:**
21
+ - Tests are executed **one at a time** (not batched)
22
+ - Quick verification focus (not exhaustive testing)
23
+ - S3 operations use `--dry-run` flag (no actual uploads/downloads)
24
+ - User can interrupt at any point with feedback
25
+
26
+ ---
27
+
28
+ ## Test Structure
29
+
30
+ ### Suite 1: Core Functionality (Baseline)
31
+ **Goal:** Verify nothing broke from original functionality
32
+
33
+ **Tests:**
34
+ 1. Brand listing (default view)
35
+ 2. Brand listing (invalid brand with error)
36
+ 3. Project listing (specific brand)
37
+ 4. Project listing (pattern matching)
38
+ 5. S3 status check
39
+ 6. Git status check
40
+
41
+ ### Suite 2: New Features (Additions)
42
+ **Goal:** Verify new capabilities work as intended
43
+
44
+ **Tests:**
45
+ 7. Brand listing (--detailed flag)
46
+ 8. Project listing (--detailed flag)
47
+ 9. Fuzzy matching (typo suggestions)
48
+ 10. Git status column (brand level)
49
+ 11. Git status column (project level)
50
+ 12. S3 sync status (3-state model)
51
+ 13. S3 timestamps (detailed view)
52
+
53
+ ### Suite 3: Edge Cases (Regressions)
54
+ **Goal:** Catch potential issues from refactoring
55
+
56
+ **Tests:**
57
+ 14. Case-insensitive brand resolution
58
+ 15. Brand shortcuts (ad, voz, joy, ss)
59
+ 16. S3 operations with no staging folder
60
+ 17. Git operations on non-git directories
61
+
62
+ ### Suite 4: Performance Check
63
+ **Goal:** Ensure no major slowdowns
64
+
65
+ **Tests:**
66
+ 18. List all brands (measure response time)
67
+ 19. List brand projects with many items
68
+ 20. Detailed view performance
69
+
70
+ ---
71
+
72
+ ## Test Execution Plan
73
+
74
+ ---
75
+
76
+ ### **SUITE 1: CORE FUNCTIONALITY (BASELINE)**
77
+
78
+ #### Test 1: Brand Listing (Default View)
79
+ **Purpose:** Verify default brand list still works
80
+ **Command:** `dam list`
81
+ **Expected:**
82
+ - Shows all brands
83
+ - Displays: BRAND, KEY, PROJECTS, SIZE, LAST MODIFIED, GIT, S3 SYNC columns
84
+ - No errors
85
+
86
+ ---
87
+
88
+ #### Test 2: Invalid Brand (Error Handling)
89
+ **Purpose:** Verify error messages work
90
+ **Command:** `dam list invalidbrand`
91
+ **Expected:**
92
+ - Error message: "Brand directory not found: invalidbrand"
93
+ - Shows available brands
94
+ - No crash
95
+
96
+ ---
97
+
98
+ #### Test 3: Project Listing (Specific Brand)
99
+ **Purpose:** Verify project listing unchanged
100
+ **Command:** `dam list appydave`
101
+ **Expected:**
102
+ - Shows all appydave projects
103
+ - Displays project names, sizes, dates
104
+ - No errors
105
+
106
+ ---
107
+
108
+ #### Test 4: Pattern Matching
109
+ **Purpose:** Verify pattern expansion works
110
+ **Command:** `dam list appydave 'b6*'`
111
+ **Expected:**
112
+ - Shows only projects matching b6* (b60-b69)
113
+ - Pattern correctly expands
114
+ - No errors
115
+
116
+ ---
117
+
118
+ #### Test 5: S3 Status Check
119
+ **Purpose:** Verify S3 status command works
120
+ **Command:** `dam s3-status appydave b65`
121
+ **Expected:**
122
+ - Shows S3 sync status for b65
123
+ - Lists files in S3 vs local
124
+ - Displays sync state (upload/download/synced)
125
+ - No errors
126
+
127
+ ---
128
+
129
+ #### Test 6: Git Status Check
130
+ **Purpose:** Verify git status detection works
131
+ **Command:** `dam status appydave`
132
+ **Expected:**
133
+ - Shows git repository status
134
+ - Shows branch, commits ahead/behind
135
+ - Shows modified/untracked files
136
+ - No errors
137
+
138
+ ---
139
+
140
+ ### **SUITE 2: NEW FEATURES (ADDITIONS)**
141
+
142
+ #### Test 7: Brand Listing (--detailed Flag)
143
+ **Purpose:** Verify new --detailed flag for brands
144
+ **Command:** `dam list --detailed`
145
+ **Expected:**
146
+ - Shows extended columns: PATH, SSD BACKUP, WORKFLOW, ACTIVE PROJECTS
147
+ - Table width approximately 200 characters
148
+ - All data populated correctly
149
+ - No errors
150
+
151
+ ---
152
+
153
+ #### Test 8: Project Listing (--detailed Flag)
154
+ **Purpose:** Verify new --detailed flag for projects
155
+ **Command:** `dam list appydave --detailed`
156
+ **Expected:**
157
+ - Shows extended columns: HEAVY/LIGHT FILES, SSD BACKUP, S3 TIMESTAMPS
158
+ - Additional metadata displayed
159
+ - No errors
160
+
161
+ ---
162
+
163
+ #### Test 9: Fuzzy Matching (Typo Suggestions)
164
+ **Purpose:** Verify "Did you mean?" feature
165
+ **Command:** `dam list appydav`
166
+ **Expected:**
167
+ - Error message shows "Did you mean?"
168
+ - Suggests "appydave"
169
+ - Helpful error format
170
+ - No crash
171
+
172
+ ---
173
+
174
+ #### Test 10: Git Status Column (Brand Level)
175
+ **Purpose:** Verify GIT STATUS column in brand list
176
+ **Command:** `dam list`
177
+ **Expected:**
178
+ - Shows "✓ clean" or "⚠️ changes" for each brand
179
+ - Accurate reflection of git state
180
+ - No errors
181
+
182
+ ---
183
+
184
+ #### Test 11: Git Status Column (Project Level)
185
+ **Purpose:** Verify GIT column in project list
186
+ **Command:** `dam list appydave`
187
+ **Expected:**
188
+ - Shows git status per project (if git repo)
189
+ - Shows "N/A" for non-git projects
190
+ - No errors
191
+
192
+ ---
193
+
194
+ #### Test 12: S3 Sync Status (3-State Model)
195
+ **Purpose:** Verify ↑ upload / ↓ download / ✓ synced states
196
+ **Command:** `dam list appydave`
197
+ **Expected:**
198
+ - S3 SYNC column shows one of: ↑ upload, ↓ download, ✓ synced, none, N/A
199
+ - Accurate reflection of sync state
200
+ - No errors
201
+
202
+ ---
203
+
204
+ #### Test 13: S3 Timestamps (Detailed View)
205
+ **Purpose:** Verify last upload/download times
206
+ **Command:** `dam list appydave --detailed`
207
+ **Expected:**
208
+ - Shows S3 LAST UPLOAD and LAST DOWNLOAD columns
209
+ - Timestamps formatted correctly
210
+ - Shows "N/A" if no S3 configured
211
+ - No errors
212
+
213
+ ---
214
+
215
+ ### **SUITE 3: EDGE CASES (REGRESSIONS)**
216
+
217
+ #### Test 14: Case-Insensitive Brand Resolution
218
+ **Purpose:** Verify case handling works
219
+ **Commands:**
220
+ - `dam list appydave`
221
+ - `dam list APPYDAVE`
222
+ - `dam list AppyDave`
223
+
224
+ **Expected:**
225
+ - All three commands work identically
226
+ - Same projects listed
227
+ - No errors
228
+
229
+ ---
230
+
231
+ #### Test 15: Brand Shortcuts
232
+ **Purpose:** Verify shortcuts resolve correctly
233
+ **Commands:**
234
+ - `dam list ad` (should resolve to appydave)
235
+ - `dam list voz` (should resolve to voz)
236
+ - `dam list joy` (should resolve to beauty-and-joy)
237
+ - `dam list ss` (should resolve to supportsignal)
238
+
239
+ **Expected:**
240
+ - Each shortcut resolves to correct brand
241
+ - Projects listed correctly
242
+ - No errors
243
+
244
+ ---
245
+
246
+ #### Test 16: S3 Operations (No Staging Folder)
247
+ **Purpose:** Verify graceful handling when s3-staging/ doesn't exist
248
+ **Command:** `dam s3-up appydave b40 --dry-run`
249
+ **Expected:**
250
+ - Clear error message OR creates staging directory
251
+ - No crash
252
+ - Helpful guidance
253
+
254
+ ---
255
+
256
+ #### Test 17: Git Operations (Non-Git Directory)
257
+ **Purpose:** Verify graceful handling of non-git projects
258
+ **Command:** `dam list appydave` (view projects with no .git)
259
+ **Expected:**
260
+ - GIT column shows "N/A" for non-git projects
261
+ - No errors
262
+ - No crash
263
+
264
+ ---
265
+
266
+ ### **SUITE 4: PERFORMANCE CHECK**
267
+
268
+ #### Test 18: List All Brands (Response Time)
269
+ **Purpose:** Ensure no major slowdown from new features
270
+ **Command:** `time dam list`
271
+ **Expected:**
272
+ - Completes in < 3 seconds (reasonable for filesystem scan)
273
+ - Output matches expected format
274
+ - No errors
275
+
276
+ **Baseline:** Original implementation time (if known)
277
+
278
+ ---
279
+
280
+ #### Test 19: List Brand Projects (Many Items)
281
+ **Purpose:** Verify performance with 20+ projects
282
+ **Command:** `time dam list appydave`
283
+ **Expected:**
284
+ - Completes in < 5 seconds
285
+ - All projects displayed
286
+ - No errors
287
+
288
+ ---
289
+
290
+ #### Test 20: Detailed View Performance
291
+ **Purpose:** Ensure --detailed flag doesn't cause excessive slowdown
292
+ **Command:** `time dam list appydave --detailed`
293
+ **Expected:**
294
+ - Completes in < 10 seconds (allows for S3 API calls)
295
+ - All data populated
296
+ - No errors
297
+
298
+ **Note:** This may be slower due to S3 API calls per project
299
+
300
+ ---
301
+
302
+ ## Success Criteria
303
+
304
+ ### Must Pass (Critical)
305
+ - ✅ All baseline tests (Suite 1) pass
306
+ - ✅ No breaking changes to existing commands
307
+ - ✅ No crashes or unhandled exceptions
308
+ - ✅ Error messages are helpful
309
+
310
+ ### Should Pass (Important)
311
+ - ✅ All new features work as designed (Suite 2)
312
+ - ✅ Edge cases handled gracefully (Suite 3)
313
+ - ✅ Performance acceptable (Suite 4)
314
+
315
+ ### Nice to Have (Enhancements)
316
+ - ✅ Fuzzy matching suggestions accurate
317
+ - ✅ Git/S3 status columns informative
318
+ - ✅ Detailed views show useful metadata
319
+
320
+ ---
321
+
322
+ ## Failure Handling
323
+
324
+ If a test fails:
325
+ 1. **Document the failure:**
326
+ - Test number
327
+ - Command run
328
+ - Expected vs actual output
329
+ - Error message (if any)
330
+
331
+ 2. **Classify severity:**
332
+ - 🔴 **CRITICAL:** Core functionality broken (Suite 1)
333
+ - 🟡 **MODERATE:** New feature not working (Suite 2)
334
+ - 🟢 **MINOR:** Edge case or performance issue (Suite 3/4)
335
+
336
+ 3. **Decision:**
337
+ - Critical failures → Stop testing, fix immediately
338
+ - Moderate failures → Document, continue testing
339
+ - Minor failures → Note for future improvement
340
+
341
+ ---
342
+
343
+ ## Test Execution Checklist
344
+
345
+ Before starting:
346
+ - [ ] Working directory: `/Users/davidcruwys/dev/ad/appydave-tools`
347
+ - [ ] Dam command available: `which dam` or `bin/dam`
348
+ - [ ] Configuration valid: `ad_config -l`
349
+ - [ ] Test brands exist: appydave, voz, etc.
350
+
351
+ During testing:
352
+ - [ ] Execute tests one at a time
353
+ - [ ] Wait for user "1" confirmation before proceeding
354
+ - [ ] Document any unexpected output
355
+ - [ ] Note performance anomalies
356
+
357
+ After testing:
358
+ - [ ] Summarize results (pass/fail counts)
359
+ - [ ] Document any critical issues
360
+ - [ ] Provide recommendations
361
+
362
+ ---
363
+
364
+ ## Notes
365
+
366
+ - **S3 Testing:** Uses `--dry-run` flag to avoid actual uploads/downloads
367
+ - **Git Testing:** Read-only operations, no repository modifications
368
+ - **Performance:** Measured with `time` command, baseline comparison if available
369
+ - **Interactive Format:** User controls pace with "1" confirmations
370
+
371
+ ---
372
+
373
+ **Test plan prepared:** 2025-01-22
374
+ **Ready to execute:** Awaiting user confirmation to begin Suite 1
@@ -91,8 +91,14 @@ module Appydave
91
91
  end
92
92
 
93
93
  unless Dir.exist?(brand_path)
94
+ # Get available brands for error message
94
95
  available = Config.available_brands_display
95
- raise BrandNotFoundError.new(brand, available)
96
+
97
+ # Use fuzzy matching to suggest similar brands
98
+ available_list = Config.available_brands
99
+ suggestions = FuzzyMatcher.find_matches(brand, available_list, threshold: 3)
100
+
101
+ raise BrandNotFoundError.new(brand, available, suggestions)
96
102
  end
97
103
 
98
104
  key
@@ -8,8 +8,16 @@ module Appydave
8
8
 
9
9
  # Raised when brand directory not found
10
10
  class BrandNotFoundError < DamError
11
- def initialize(brand, available_brands = nil)
11
+ def initialize(brand, available_brands = nil, suggestions = nil)
12
12
  message = "Brand directory not found: #{brand}"
13
+
14
+ # Add fuzzy match suggestions if provided
15
+ if suggestions && !suggestions.empty?
16
+ message += "\n\nDid you mean?"
17
+ suggestions.each { |s| message += "\n - #{s}" }
18
+ end
19
+
20
+ # Add full list of available brands
13
21
  message += "\n\nAvailable brands:\n#{available_brands}" if available_brands && !available_brands.empty?
14
22
  super(message)
15
23
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appydave
4
+ module Tools
5
+ module Dam
6
+ # Fuzzy matching for brand names using Levenshtein distance
7
+ class FuzzyMatcher
8
+ class << self
9
+ # Find closest matches to input string
10
+ # @param input [String] Input string to match
11
+ # @param candidates [Array<String>] List of valid options
12
+ # @param threshold [Integer] Maximum distance to consider a match (default: 3)
13
+ # @return [Array<String>] Sorted list of closest matches
14
+ def find_matches(input, candidates, threshold: 3)
15
+ return [] if input.nil? || input.empty? || candidates.empty?
16
+
17
+ # Calculate distances and filter by threshold
18
+ matches = candidates.map do |candidate|
19
+ distance = levenshtein_distance(input.downcase, candidate.downcase)
20
+ { candidate: candidate, distance: distance }
21
+ end
22
+
23
+ # Filter by threshold
24
+ matches = matches.select { |m| m[:distance] <= threshold }
25
+
26
+ # Sort by distance (closest first)
27
+ matches.sort_by { |m| m[:distance] }.map { |m| m[:candidate] }
28
+ end
29
+
30
+ # Calculate Levenshtein distance between two strings
31
+ # @param str1 [String] First string
32
+ # @param str2 [String] Second string
33
+ # @return [Integer] Edit distance
34
+ def levenshtein_distance(str1, str2)
35
+ return str2.length if str1.empty?
36
+ return str1.length if str2.empty?
37
+
38
+ # Create distance matrix
39
+ matrix = Array.new(str1.length + 1) { Array.new(str2.length + 1) }
40
+
41
+ # Initialize first row and column
42
+ (0..str1.length).each { |i| matrix[i][0] = i }
43
+ (0..str2.length).each { |j| matrix[0][j] = j }
44
+
45
+ # Calculate distances
46
+ (1..str1.length).each do |i|
47
+ (1..str2.length).each do |j|
48
+ cost = str1[i - 1] == str2[j - 1] ? 0 : 1
49
+ matrix[i][j] = [
50
+ matrix[i - 1][j] + 1, # deletion
51
+ matrix[i][j - 1] + 1, # insertion
52
+ matrix[i - 1][j - 1] + cost # substitution
53
+ ].min
54
+ end
55
+ end
56
+
57
+ matrix[str1.length][str2.length]
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end