@@ -2,37 +2,20 @@ import test from "ava";
22import path from "node:path" ;
33import fs from "node:fs/promises" ;
44import { rimraf } from "rimraf" ;
5- import { cleanCache } from "../../../lib/cache/CacheCleanup.js" ;
5+ import { cleanCache , getCacheInfo } from "../../../lib/cache/CacheCleanup.js" ;
66
77const TEST_DIR = path . join ( import . meta. dirname , ".." , ".." , "tmp" , "CacheCleanup" ) ;
88
99test . after . always ( async ( ) => {
1010 await rimraf ( TEST_DIR ) . catch ( ( ) => { } ) ;
1111} ) ;
1212
13- /**
14- * Create a unique test directory for each test.
15- *
16- * @param {object } t AVA test context
17- * @returns {string } Path to the ui5DataDir fixture
18- */
1913function createTestDir ( t ) {
2014 const dir = path . join ( TEST_DIR , `test-${ Date . now ( ) } -${ Math . random ( ) . toString ( 36 ) . slice ( 2 ) } ` ) ;
2115 t . context . ui5DataDir = dir ;
2216 return dir ;
2317}
2418
25- /**
26- * Create a framework package fixture.
27- *
28- * @param {string } ui5DataDir Base data directory
29- * @param {string } scope Package scope (e.g., "@openui5")
30- * @param {string } name Package name (e.g., "sap.ui.core")
31- * @param {string } version Version string
32- * @param {object } [options]
33- * @param {Date } [options.mtime] Custom mtime for the package file
34- * @returns {Promise<void> }
35- */
3619async function createPackage ( ui5DataDir , scope , name , version , { mtime} = { } ) {
3720 const pkgDir = path . join ( ui5DataDir , "framework" , "packages" , scope , name , version ) ;
3821 await fs . mkdir ( pkgDir , { recursive : true } ) ;
@@ -43,7 +26,7 @@ async function createPackage(ui5DataDir, scope, name, version, {mtime} = {}) {
4326 }
4427}
4528
46- // ===== cleanCache: empty/nonexistent dir =====
29+ // ===== cleanCache tests =====
4730
4831test ( "cleanCache: returns empty result for nonexistent directory" , async ( t ) => {
4932 const result = await cleanCache ( { ui5DataDir : "/tmp/nonexistent-ui5-dir-test" } ) ;
@@ -52,8 +35,6 @@ test("cleanCache: returns empty result for nonexistent directory", async (t) =>
5235 t . deepEqual ( result . entries , [ ] ) ;
5336} ) ;
5437
55- // ===== cleanCache: clean all =====
56-
5738test ( "cleanCache: clean all removes framework packages" , async ( t ) => {
5839 const ui5DataDir = createTestDir ( t ) ;
5940 await createPackage ( ui5DataDir , "@openui5" , "sap.ui.core" , "1.120.0" ) ;
@@ -67,14 +48,11 @@ test("cleanCache: clean all removes framework packages", async (t) => {
6748 t . is ( frameworkEntries [ 0 ] . path , "framework" ) ;
6849} ) ;
6950
70- // ===== cleanCache: build cache (full clean) =====
71-
7251test ( "cleanCache: clean all clears buildCache database" , async ( t ) => {
7352 const ui5DataDir = createTestDir ( t ) ;
7453 const buildCacheDir = path . join ( ui5DataDir , "buildCache" , "v0_7" ) ;
7554 await fs . mkdir ( buildCacheDir , { recursive : true } ) ;
7655
77- // Create a real SQLite database with tables and some data
7856 const { DatabaseSync} = await import ( "node:sqlite" ) ;
7957 const dbPath = path . join ( buildCacheDir , "cache.db" ) ;
8058 const db = new DatabaseSync ( dbPath ) ;
@@ -95,15 +73,195 @@ test("cleanCache: clean all clears buildCache database", async (t) => {
9573 const buildCacheEntry = result . entries . find ( ( e ) => e . type === "buildCache" ) ;
9674 t . truthy ( buildCacheEntry ) ;
9775
98- // Verify directory and DB file still exist
9976 await fs . access ( buildCacheDir ) ;
10077 await fs . access ( dbPath ) ;
10178
102- // Verify tables are empty
10379 const dbAfter = new DatabaseSync ( dbPath ) ;
10480 const contentCount = dbAfter . prepare ( "SELECT COUNT(*) as count FROM content" ) . get ( ) . count ;
10581 const indexCount = dbAfter . prepare ( "SELECT COUNT(*) as count FROM index_cache" ) . get ( ) . count ;
10682 t . is ( contentCount , 0 ) ;
10783 t . is ( indexCount , 0 ) ;
10884 dbAfter . close ( ) ;
10985} ) ;
86+
87+ test ( "cleanCache: skips empty framework directory" , async ( t ) => {
88+ const ui5DataDir = createTestDir ( t ) ;
89+ const frameworkDir = path . join ( ui5DataDir , "framework" ) ;
90+ await fs . mkdir ( frameworkDir , { recursive : true } ) ;
91+
92+ const result = await cleanCache ( { ui5DataDir} ) ;
93+
94+ t . is ( result . totalCount , 0 ) ;
95+ const frameworkEntries = result . entries . filter ( ( e ) => e . type === "framework" ) ;
96+ t . is ( frameworkEntries . length , 0 ) ;
97+ } ) ;
98+
99+ test ( "cleanCache: cleans both framework and build cache" , async ( t ) => {
100+ const ui5DataDir = createTestDir ( t ) ;
101+
102+ await createPackage ( ui5DataDir , "@openui5" , "sap.ui.core" , "1.120.0" ) ;
103+
104+ const buildCacheDir = path . join ( ui5DataDir , "buildCache" , "v0_7" ) ;
105+ await fs . mkdir ( buildCacheDir , { recursive : true } ) ;
106+ const { DatabaseSync} = await import ( "node:sqlite" ) ;
107+ const dbPath = path . join ( buildCacheDir , "cache.db" ) ;
108+ const db = new DatabaseSync ( dbPath ) ;
109+ db . exec ( `
110+ CREATE TABLE content (integrity TEXT);
111+ CREATE TABLE index_cache (project_id TEXT);
112+ CREATE TABLE stage_metadata (project_id TEXT);
113+ CREATE TABLE task_metadata (project_id TEXT);
114+ CREATE TABLE result_metadata (project_id TEXT);
115+ ` ) ;
116+ db . exec ( "INSERT INTO content VALUES ('test')" ) ;
117+ db . close ( ) ;
118+
119+ const result = await cleanCache ( { ui5DataDir} ) ;
120+
121+ t . true ( result . totalCount >= 1 ) ; // At least framework
122+ t . truthy ( result . entries . find ( ( e ) => e . type === "framework" ) ) ;
123+ t . true ( result . totalSize > 0 ) ;
124+ // Build cache may also be cleaned
125+ if ( result . totalCount === 2 ) {
126+ t . truthy ( result . entries . find ( ( e ) => e . type === "buildCache" ) ) ;
127+ }
128+ } ) ;
129+
130+ test ( "cleanCache: handles corrupted database gracefully" , async ( t ) => {
131+ const ui5DataDir = createTestDir ( t ) ;
132+ const buildCacheDir = path . join ( ui5DataDir , "buildCache" , "v0_7" ) ;
133+ await fs . mkdir ( buildCacheDir , { recursive : true } ) ;
134+
135+ await fs . writeFile ( path . join ( buildCacheDir , "cache.db" ) , "not a valid database" ) ;
136+
137+ const result = await cleanCache ( { ui5DataDir} ) ;
138+
139+ t . pass ( ) ;
140+ const buildEntries = result . entries . filter ( ( e ) => e . type === "buildCache" ) ;
141+ t . is ( buildEntries . length , 0 ) ;
142+ } ) ;
143+
144+ // ===== getCacheInfo tests =====
145+
146+ test ( "getCacheInfo: returns empty array for nonexistent directory" , async ( t ) => {
147+ const items = await getCacheInfo ( { ui5DataDir : "/tmp/nonexistent-ui5-dir-test" } ) ;
148+ t . deepEqual ( items , [ ] ) ;
149+ } ) ;
150+
151+ test ( "getCacheInfo: detects framework cache with size" , async ( t ) => {
152+ const ui5DataDir = createTestDir ( t ) ;
153+ await createPackage ( ui5DataDir , "@openui5" , "sap.ui.core" , "1.120.0" ) ;
154+
155+ const items = await getCacheInfo ( { ui5DataDir} ) ;
156+
157+ const frameworkItem = items . find ( ( item ) => item . path === "framework/" ) ;
158+ t . truthy ( frameworkItem ) ;
159+ t . true ( frameworkItem . size > 0 ) ;
160+ t . is ( frameworkItem . type , "directory" ) ;
161+ } ) ;
162+
163+ test ( "getCacheInfo: skips empty framework directory" , async ( t ) => {
164+ const ui5DataDir = createTestDir ( t ) ;
165+ const frameworkDir = path . join ( ui5DataDir , "framework" ) ;
166+ await fs . mkdir ( frameworkDir , { recursive : true } ) ;
167+
168+ const items = await getCacheInfo ( { ui5DataDir} ) ;
169+
170+ const frameworkItem = items . find ( ( item ) => item . path === "framework/" ) ;
171+ t . falsy ( frameworkItem ) ;
172+ } ) ;
173+
174+ test ( "getCacheInfo: detects build cache with records" , async ( t ) => {
175+ const ui5DataDir = createTestDir ( t ) ;
176+ const buildCacheDir = path . join ( ui5DataDir , "buildCache" , "v0_7" ) ;
177+ await fs . mkdir ( buildCacheDir , { recursive : true } ) ;
178+
179+ const { DatabaseSync} = await import ( "node:sqlite" ) ;
180+ const dbPath = path . join ( buildCacheDir , "cache.db" ) ;
181+ const db = new DatabaseSync ( dbPath ) ;
182+ db . exec ( `
183+ CREATE TABLE content (integrity TEXT PRIMARY KEY, data BLOB);
184+ CREATE TABLE index_cache (project_id TEXT, build_signature TEXT, kind TEXT, data BLOB);
185+ CREATE TABLE stage_metadata
186+ (project_id TEXT, build_signature TEXT, stage_id TEXT, stage_signature TEXT, data BLOB);
187+ CREATE TABLE task_metadata (project_id TEXT, build_signature TEXT, task_name TEXT, type TEXT, data BLOB);
188+ CREATE TABLE result_metadata (project_id TEXT, build_signature TEXT, stage_signature TEXT, data BLOB);
189+ ` ) ;
190+ db . exec ( "INSERT INTO content VALUES ('test-integrity', 'test-data')" ) ;
191+ db . close ( ) ;
192+
193+ const items = await getCacheInfo ( { ui5DataDir} ) ;
194+
195+ const buildItem = items . find ( ( item ) => item . type === "database" ) ;
196+ t . truthy ( buildItem ) ;
197+ t . is ( buildItem . path , "buildCache/v0_7 (database records)" ) ;
198+ t . true ( buildItem . size > 0 ) ;
199+ } ) ;
200+
201+ test ( "getCacheInfo: skips build cache with no records" , async ( t ) => {
202+ const ui5DataDir = createTestDir ( t ) ;
203+ const buildCacheDir = path . join ( ui5DataDir , "buildCache" , "v0_7" ) ;
204+ await fs . mkdir ( buildCacheDir , { recursive : true } ) ;
205+
206+ const { DatabaseSync} = await import ( "node:sqlite" ) ;
207+ const dbPath = path . join ( buildCacheDir , "cache.db" ) ;
208+ const db = new DatabaseSync ( dbPath ) ;
209+ db . exec ( `
210+ CREATE TABLE content (integrity TEXT PRIMARY KEY, data BLOB);
211+ CREATE TABLE index_cache (project_id TEXT, build_signature TEXT, kind TEXT, data BLOB);
212+ CREATE TABLE stage_metadata
213+ (project_id TEXT, build_signature TEXT, stage_id TEXT, stage_signature TEXT, data BLOB);
214+ CREATE TABLE task_metadata (project_id TEXT, build_signature TEXT, task_name TEXT, type TEXT, data BLOB);
215+ CREATE TABLE result_metadata (project_id TEXT, build_signature TEXT, stage_signature TEXT, data BLOB);
216+ ` ) ;
217+ db . close ( ) ;
218+
219+ const items = await getCacheInfo ( { ui5DataDir} ) ;
220+
221+ const buildItem = items . find ( ( item ) => item . type === "database" ) ;
222+ t . falsy ( buildItem ) ;
223+ } ) ;
224+
225+ test ( "getCacheInfo: handles corrupted database gracefully" , async ( t ) => {
226+ const ui5DataDir = createTestDir ( t ) ;
227+ const buildCacheDir = path . join ( ui5DataDir , "buildCache" , "v0_7" ) ;
228+ await fs . mkdir ( buildCacheDir , { recursive : true } ) ;
229+
230+ await fs . writeFile ( path . join ( buildCacheDir , "cache.db" ) , "not a valid database" ) ;
231+
232+ const items = await getCacheInfo ( { ui5DataDir} ) ;
233+
234+ t . pass ( ) ;
235+ const buildItem = items . find ( ( item ) => item . type === "database" ) ;
236+ t . falsy ( buildItem ) ;
237+ } ) ;
238+
239+ test ( "getCacheInfo: detects both framework and build cache" , async ( t ) => {
240+ const ui5DataDir = createTestDir ( t ) ;
241+
242+ await createPackage ( ui5DataDir , "@openui5" , "sap.ui.core" , "1.120.0" ) ;
243+
244+ const buildCacheDir = path . join ( ui5DataDir , "buildCache" , "v0_7" ) ;
245+ await fs . mkdir ( buildCacheDir , { recursive : true } ) ;
246+ const { DatabaseSync} = await import ( "node:sqlite" ) ;
247+ const dbPath = path . join ( buildCacheDir , "cache.db" ) ;
248+ const db = new DatabaseSync ( dbPath ) ;
249+ db . exec ( `
250+ CREATE TABLE content (integrity TEXT);
251+ CREATE TABLE index_cache (project_id TEXT);
252+ CREATE TABLE stage_metadata (project_id TEXT);
253+ CREATE TABLE task_metadata (project_id TEXT);
254+ CREATE TABLE result_metadata (project_id TEXT);
255+ ` ) ;
256+ db . exec ( "INSERT INTO content VALUES ('test')" ) ;
257+ db . close ( ) ;
258+
259+ const items = await getCacheInfo ( { ui5DataDir} ) ;
260+
261+ t . true ( items . length >= 1 ) ; // At least framework
262+ t . truthy ( items . find ( ( item ) => item . path === "framework/" ) ) ;
263+ // Build cache may also be detected
264+ if ( items . length === 2 ) {
265+ t . truthy ( items . find ( ( item ) => item . type === "database" ) ) ;
266+ }
267+ } ) ;
0 commit comments