@drax/settings-back 0.11.3

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.
Files changed (52) hide show
  1. package/dist/controller/SettingController.js +89 -0
  2. package/dist/factory/SettingServiceFactory.js +24 -0
  3. package/dist/index.js +8 -0
  4. package/dist/interfaces/ISettingRepository.js +1 -0
  5. package/dist/model/SettingsModel.js +28 -0
  6. package/dist/permissions/SettingPermissions.js +8 -0
  7. package/dist/repository/mongo/SettingMongoRepository.js +13 -0
  8. package/dist/repository/sqlite/SettingSqliteRepository.js +113 -0
  9. package/dist/routes/SettingRoutes.js +10 -0
  10. package/dist/schemas/SettingSchema.js +7 -0
  11. package/dist/services/SettingService.js +95 -0
  12. package/package.json +66 -0
  13. package/src/controller/SettingController.ts +92 -0
  14. package/src/factory/SettingServiceFactory.ts +32 -0
  15. package/src/index.ts +24 -0
  16. package/src/interfaces/ISettingRepository.ts +6 -0
  17. package/src/model/SettingsModel.ts +46 -0
  18. package/src/permissions/SettingPermissions.ts +9 -0
  19. package/src/repository/mongo/SettingMongoRepository.ts +23 -0
  20. package/src/repository/sqlite/SettingSqliteRepository.ts +176 -0
  21. package/src/routes/SettingRoutes.ts +17 -0
  22. package/src/schemas/SettingSchema.ts +11 -0
  23. package/src/services/SettingService.ts +111 -0
  24. package/test/repository/mongo/MongoInMemory.ts +43 -0
  25. package/test/repository/mongo/SettingMongoRepository.test.ts +180 -0
  26. package/test/repository/sqlite/SettingSqliteRepository.test.ts +175 -0
  27. package/test/services/SettingService.test.ts +319 -0
  28. package/test.db +0 -0
  29. package/tsconfig.json +16 -0
  30. package/tsconfig.tsbuildinfo +1 -0
  31. package/types/controller/SettingController.d.ts +15 -0
  32. package/types/controller/SettingController.d.ts.map +1 -0
  33. package/types/factory/SettingServiceFactory.d.ts +4 -0
  34. package/types/factory/SettingServiceFactory.d.ts.map +1 -0
  35. package/types/index.d.ts +11 -0
  36. package/types/index.d.ts.map +1 -0
  37. package/types/interfaces/ISettingRepository.d.ts +6 -0
  38. package/types/interfaces/ISettingRepository.d.ts.map +1 -0
  39. package/types/model/SettingsModel.d.ts +19 -0
  40. package/types/model/SettingsModel.d.ts.map +1 -0
  41. package/types/permissions/SettingPermissions.d.ts +8 -0
  42. package/types/permissions/SettingPermissions.d.ts.map +1 -0
  43. package/types/repository/mongo/SettingMongoRepository.d.ts +9 -0
  44. package/types/repository/mongo/SettingMongoRepository.d.ts.map +1 -0
  45. package/types/repository/sqlite/SettingSqliteRepository.d.ts +22 -0
  46. package/types/repository/sqlite/SettingSqliteRepository.d.ts.map +1 -0
  47. package/types/routes/SettingRoutes.d.ts +4 -0
  48. package/types/routes/SettingRoutes.d.ts.map +1 -0
  49. package/types/schemas/SettingSchema.d.ts +10 -0
  50. package/types/schemas/SettingSchema.d.ts.map +1 -0
  51. package/types/services/SettingService.d.ts +23 -0
  52. package/types/services/SettingService.d.ts.map +1 -0
@@ -0,0 +1,46 @@
1
+ import {mongoose} from '@drax/common-back';
2
+ import uniqueValidator from 'mongoose-unique-validator';
3
+ import {ISetting} from "@drax/settings-share";
4
+
5
+
6
+ const SettingSchema = new mongoose.Schema<ISetting>({
7
+
8
+ key: {type: String, required: true, unique: true},
9
+ value: {type: mongoose.Schema.Types.Mixed, required: false, unique: false},
10
+ //valueList: [{type: String, required: false, unique: false}],
11
+ label: {type: String, required: false},
12
+ group: {type: String, required: true},
13
+ type: {type: String, default: "string", enum: ['string','longString','number','enum','boolean', 'password', 'stringList','numberList', 'enumList', 'ref', 'secret'], required: false, unique: false},
14
+ options: [{type: String}],
15
+ regex: {type: String},
16
+ entity: {type: String, required: false},
17
+ entityValue: {type: String, required: false},
18
+ entityText: {type: String, required: false, unique: false},
19
+ prefix: {type: String, required: false},
20
+ suffix: {type: String, required: false},
21
+ })
22
+
23
+ SettingSchema.virtual("id").get(function () {
24
+ return this._id.toString();
25
+ });
26
+
27
+
28
+ SettingSchema.set('toJSON', {getters: true, virtuals: true});
29
+
30
+ SettingSchema.set('toObject', {getters: true, virtuals: true});
31
+
32
+ SettingSchema.plugin(uniqueValidator, {message: 'validation.unique'});
33
+
34
+
35
+
36
+ const MODEL_NAME = 'Setting';
37
+ const COLLECTION_NAME = 'settings';
38
+
39
+ const SettingModel = mongoose.model<ISetting>(MODEL_NAME, SettingSchema,COLLECTION_NAME);
40
+
41
+ export {
42
+ SettingSchema,
43
+ SettingModel
44
+ }
45
+
46
+ export default SettingModel
@@ -0,0 +1,9 @@
1
+ enum SettingPermissions {
2
+ Update = "setting:update",
3
+ View = "setting:view",
4
+ Manage = "setting:manage",
5
+
6
+ }
7
+
8
+ export default SettingPermissions;
9
+ export {SettingPermissions};
@@ -0,0 +1,23 @@
1
+ import {AbstractMongoRepository} from "@drax/crud-back";
2
+ import type {ISettingRepository} from "../../interfaces/ISettingRepository";
3
+ import {SettingModel} from "../../model/SettingsModel.js";
4
+ import {ISetting, ISettingBase} from "@drax/settings-share";
5
+
6
+
7
+ class SettingMongoRepository extends AbstractMongoRepository<ISetting,ISettingBase,ISettingBase> implements ISettingRepository {
8
+
9
+
10
+ constructor() {
11
+ super();
12
+ //@ts-ignore
13
+ this._model = SettingModel;
14
+
15
+ this._searchFields = ['_id', 'key'];
16
+ this._populateFields = [];
17
+ }
18
+
19
+
20
+ }
21
+
22
+ export default SettingMongoRepository;
23
+ export {SettingMongoRepository};
@@ -0,0 +1,176 @@
1
+ import {ISettingRepository} from '../../interfaces/ISettingRepository'
2
+ import {UUID} from "crypto";
3
+ import sqlite from "better-sqlite3";
4
+ import {randomUUID} from "node:crypto";
5
+ import {IDraxPaginateResult, IDraxPaginateOptions} from "@drax/crud-share";
6
+ import {ISetting, ISettingBase} from "@drax/settings-share";
7
+ import {
8
+ SqliteErrorToValidationError,
9
+ SqliteTableBuilder,
10
+ SqliteTableField,
11
+ SqlQueryFilter,
12
+ SqlSort
13
+ } from "@drax/common-back";
14
+
15
+ const tableFields: SqliteTableField[] = [
16
+ {name: "key", type: "TEXT", unique: true, primary: false},
17
+ {name: "value", type: "TEXT", unique: false, primary: false},
18
+ ]
19
+
20
+
21
+ class SettingSqliteRepository implements ISettingRepository{
22
+
23
+ protected db: any;
24
+ protected dataBaseFile: string;
25
+ protected _searchFields: string[] = []
26
+
27
+ constructor(dataBaseFile:string, verbose:boolean = false) {
28
+ this.dataBaseFile = dataBaseFile;
29
+ this._searchFields = ['_id', 'key'];
30
+ this.db = new sqlite(dataBaseFile, {verbose: verbose ? console.log : null});
31
+ this.table()
32
+ }
33
+
34
+ table() {
35
+ const builder = new SqliteTableBuilder(
36
+ this.dataBaseFile,
37
+ 'settings',
38
+ tableFields,
39
+ false);
40
+ builder.build('id')
41
+ }
42
+
43
+
44
+
45
+ async create(data: ISettingBase): Promise<ISetting> {
46
+ try{
47
+
48
+ if(!data.id){
49
+ data.id = randomUUID()
50
+ }
51
+
52
+ // data.createdAt = (new Date().toISOString())
53
+
54
+ const fields = Object.keys(data)
55
+ .map(field => `${field}`)
56
+ .join(', ');
57
+
58
+ const values = Object.keys(data)
59
+ .map(field => `@${field}`)
60
+ .join(', ');
61
+
62
+
63
+ const stmt = this.db.prepare(`INSERT INTO settings (${fields}) VALUES (${values})`);
64
+ stmt.run(data)
65
+ return this.findById(data.id as UUID)
66
+ }catch (e){
67
+ console.log(e)
68
+ throw SqliteErrorToValidationError(e, data)
69
+ }
70
+ }
71
+
72
+
73
+
74
+ async update(id: string, data: ISettingBase): Promise<ISetting> {
75
+ try{
76
+ // data.updatedAt = (new Date().toISOString())
77
+
78
+ const setClauses = Object.keys(data)
79
+ .map(field => `${field} = @${field}`)
80
+ .join(', ');
81
+
82
+ data.id = id
83
+
84
+ const stmt = this.db.prepare( `UPDATE settings SET ${setClauses} WHERE id = @id `);
85
+
86
+ stmt.run(data);
87
+
88
+ return this.findById(id)
89
+ }catch (e){
90
+ console.log(e)
91
+ throw SqliteErrorToValidationError(e, data)
92
+ }
93
+
94
+ }
95
+
96
+ async paginate({
97
+ page= 1,
98
+ limit= 5,
99
+ orderBy= '',
100
+ order= false,
101
+ search= '',
102
+ filters= []} : IDraxPaginateOptions): Promise<IDraxPaginateResult<ISetting>>{
103
+ const offset = page > 1 ? (page - 1) * limit : 0
104
+
105
+ let where=""
106
+ if (search) {
107
+ where = ` WHERE name LIKE '%${search}%'`
108
+ }
109
+
110
+ where = SqlQueryFilter.applyFilters(where, filters)
111
+ const sort = SqlSort.applySort(orderBy, order)
112
+
113
+ const rCount = this.db.prepare('SELECT COUNT(*) as count FROM settings'+where).get();
114
+ where += sort
115
+ const settings = this.db.prepare('SELECT * FROM settings ' + where + ' LIMIT ? OFFSET ?').all([limit, offset]);
116
+
117
+
118
+ return {
119
+ page: page,
120
+ limit: limit,
121
+ total: rCount.count,
122
+ items: settings
123
+ }
124
+ }
125
+
126
+ async delete(id: string): Promise<boolean> {
127
+ const stmt = this.db.prepare('DELETE FROM settings WHERE id = ?');
128
+ stmt.run(id);
129
+ return true
130
+ }
131
+
132
+ async deleteAll(): Promise<boolean> {
133
+ const stmt = this.db.prepare('DELETE FROM settings');
134
+ stmt.run();
135
+ return true
136
+ }
137
+
138
+ async findById(id: string): Promise<ISetting | null>{
139
+ const setting = this.db.prepare('SELECT * FROM settings WHERE id = ?').get(id);
140
+ if(setting){
141
+ return setting
142
+ }
143
+ return undefined
144
+ }
145
+
146
+ async findByKey(key: string): Promise<ISetting | null>{
147
+ const setting = this.db.prepare('SELECT * FROM settings WHERE key = ?').get(key);
148
+ if(setting){
149
+ return setting
150
+ }
151
+ return undefined
152
+ }
153
+
154
+ async fetchAll(): Promise<ISetting[]>{
155
+ const settings = this.db.prepare('SELECT * FROM settings').all();
156
+ return settings
157
+ }
158
+
159
+
160
+ async search(value: any, limit: number = 1000): Promise<any[]>{
161
+ let where=""
162
+ if (value && this._searchFields.length > 0) {
163
+ where = ` WHERE ${this._searchFields.map(field => `${field} LIKE '%${value}%'`).join(" OR ")}`
164
+ }
165
+ const items = this.db.prepare(`SELECT * FROM settings ${where}`).all();
166
+ return items
167
+ }
168
+
169
+
170
+
171
+
172
+
173
+ }
174
+
175
+ export default SettingSqliteRepository
176
+ export {SettingSqliteRepository}
@@ -0,0 +1,17 @@
1
+ import SettingController from "../controller/SettingController.js";
2
+
3
+
4
+ async function SettingsRoutes(fastify, options) {
5
+ const controller: SettingController = new SettingController()
6
+
7
+ fastify.get('/api/settings', (req,rep) => controller.fetchAll(req,rep))
8
+
9
+ fastify.get('/api/settings/grouped', (req,rep) => controller.fetchGrouped(req,rep))
10
+
11
+ fastify.get('/api/settings/:key', (req,rep) => controller.findByKey(req,rep))
12
+
13
+ fastify.patch('/api/settings/:id', (req,rep) => controller.updateValue(req,rep))
14
+ }
15
+
16
+ export default SettingsRoutes
17
+ export {SettingsRoutes}
@@ -0,0 +1,11 @@
1
+ import { object, string } from "zod"
2
+
3
+ const settingSchema = object({
4
+ key: string({ required_error: "validation.required" })
5
+ .min(1, "validation.required"),
6
+ })
7
+
8
+
9
+ export default settingSchema
10
+
11
+ export {settingSchema}
@@ -0,0 +1,111 @@
1
+ import {ISettingRepository} from "../interfaces/ISettingRepository";
2
+ import {settingSchema} from "../schemas/SettingSchema.js";
3
+ import {ISetting, ISettingBase} from "@drax/settings-share";
4
+ import {AbstractService} from "@drax/crud-back";
5
+ import {DraxCache, ZodErrorToValidationError} from "@drax/common-back";
6
+ import {ZodError} from "zod";
7
+
8
+ class SettingService extends AbstractService<ISetting, ISettingBase, ISettingBase> {
9
+
10
+ protected _repository: ISettingRepository
11
+ protected _cache: DraxCache<ISetting> = new DraxCache()
12
+
13
+ constructor(repostitory: ISettingRepository) {
14
+ super(repostitory, settingSchema)
15
+ this._repository = repostitory
16
+ console.log("SettingService constructor")
17
+ }
18
+
19
+
20
+ async cache(key: string): Promise<ISetting> {
21
+ return this._cache.getOrLoad(key,
22
+ async () => {
23
+ return await this.findByKey(key)
24
+ },
25
+ 20000)
26
+ }
27
+
28
+ async cacheValue(key: string): Promise<any> {
29
+ let setting = await this.cache(key)
30
+ return setting?.value
31
+ }
32
+
33
+ async findByKey(key: string): Promise<ISetting | undefined> {
34
+ return this._repository.findOneBy("key", key)
35
+ }
36
+
37
+ async fetchAll(): Promise<ISetting[]> {
38
+ return await this._repository.fetchAll()
39
+ }
40
+
41
+ async fetchGrouped(): Promise<{ [key: string]: ISetting[] }> {
42
+ const settings = await this._repository.fetchAll()
43
+ return settings.reduce((acc, setting) => {
44
+ if (!acc[setting.group]) {
45
+ acc[setting.group] = [];
46
+ }
47
+ acc[setting.group].push(setting);
48
+ return acc;
49
+ }, {} as { [key: string]: ISetting[] });
50
+ }
51
+
52
+ async updateValue(id: string, value: string): Promise<ISetting | undefined> {
53
+ return this._repository.updatePartial(id, {value})
54
+ }
55
+
56
+ async create(data: ISettingBase): Promise<ISetting> {
57
+ try {
58
+ await settingSchema.parseAsync(data)
59
+ if (Array.isArray(data.value)) {
60
+ data.value = JSON.stringify(data.value)
61
+ }
62
+ const setting = await this._repository.create(data)
63
+ return setting
64
+ } catch (e) {
65
+ console.error("Error on create setting", e)
66
+ if (e instanceof ZodError) {
67
+ throw ZodErrorToValidationError(e, data)
68
+ }
69
+ throw e
70
+ }
71
+ }
72
+
73
+ async update(id: string, data: ISettingBase): Promise<ISetting> {
74
+ try {
75
+ await settingSchema.parseAsync(data)
76
+ //Borramos el valor para evitar el conflicto de actualización
77
+ delete data.value
78
+ const setting = await this._repository.update(id, data)
79
+ return setting
80
+ } catch (e) {
81
+ console.error("Error on update setting", e)
82
+ if (e instanceof ZodError) {
83
+ throw ZodErrorToValidationError(e, data)
84
+ }
85
+ throw e
86
+ }
87
+ }
88
+
89
+ async createOrUpdate(data: ISettingBase): Promise<ISetting> {
90
+ try {
91
+ await settingSchema.parseAsync(data)
92
+ const setting = await this._repository.findOneBy("key", data.key)
93
+ if (setting) {
94
+ delete data.value
95
+ return await this._repository.updatePartial(setting.id, data)
96
+ } else {
97
+ return await this._repository.create(data)
98
+ }
99
+ } catch (e) {
100
+ console.error("Error on create or update setting", e)
101
+ if (e instanceof ZodError) {
102
+ throw ZodErrorToValidationError(e, data)
103
+ }
104
+ throw e
105
+ }
106
+ }
107
+
108
+ }
109
+
110
+ export default SettingService
111
+ export {SettingService}
@@ -0,0 +1,43 @@
1
+ import {mongoose} from '@drax/common-back';
2
+ import { MongoMemoryServer } from 'mongodb-memory-server';
3
+
4
+ let mongoServer
5
+
6
+ class MongoInMemory{
7
+
8
+ static async connect(){
9
+ mongoServer = await MongoMemoryServer.create();
10
+ if(mongoServer.state == "new"){
11
+ await mongoServer.start()
12
+ }
13
+ if(!mongoose.connection.readyState){
14
+ await mongoose.connect(mongoServer.getUri(), { dbName: "verifyMASTER" });
15
+ }
16
+ return
17
+ }
18
+ static get mongooseStatus(){
19
+ return mongoose.connection.readyState
20
+ }
21
+
22
+ static get serverStatus(){
23
+ return mongoServer.state
24
+ }
25
+
26
+ static get status(){
27
+ return mongoose.connection.readyState
28
+ }
29
+
30
+ static async disconnect(){
31
+ await mongoose.disconnect();
32
+ }
33
+
34
+ static async DropAndClose(){
35
+ if (mongoServer) {
36
+ await mongoose.connection.dropDatabase();
37
+ await mongoose.connection.close();
38
+ await mongoServer.stop();
39
+ }
40
+ }
41
+ }
42
+
43
+ export default MongoInMemory
@@ -0,0 +1,180 @@
1
+ import {describe, beforeAll, it, vi, expect} from "vitest"
2
+ import {SettingMongoRepository} from "../../../src/repository/mongo/SettingMongoRepository.js";
3
+ import {ISetting, ISettingBase} from "@drax/settings-share";
4
+ import {AbstractMongoRepository} from "@drax/crud-back";
5
+ import MongoInMemory from "./MongoInMemory.js";
6
+
7
+ describe('SettingMongoRepository', () => {
8
+
9
+ beforeAll(async () => {
10
+ await MongoInMemory.connect();
11
+ })
12
+
13
+
14
+ it('should initialize _searchFields with the correct default values', () => {
15
+ // Arrange & Act
16
+ const repository = new SettingMongoRepository();
17
+
18
+ // Assert
19
+ expect(repository._searchFields).toEqual(['_id', 'key']);
20
+ });
21
+
22
+ it('should verify that SettingMongoRepository is an instance of AbstractMongoRepository', () => {
23
+ // Arrange
24
+ const repository = new SettingMongoRepository();
25
+
26
+ // Assert
27
+ expect(repository).toBeInstanceOf(AbstractMongoRepository);
28
+ });
29
+
30
+
31
+
32
+ it('should correctly handle adding a new setting to the repository', async () => {
33
+ // Arrange
34
+ const repository = new SettingMongoRepository();
35
+ const newSetting: ISettingBase = {
36
+ key: 'newKey',
37
+ value: 'newValue',
38
+ label: 'New Key Label',
39
+ group: 'Default Group',
40
+ type: 'string',
41
+ options: []
42
+ };
43
+ const expectedSetting = {id: '2', ...newSetting};
44
+
45
+ // Mock the create method of the repository
46
+ vi.spyOn(repository, 'create').mockResolvedValue(expectedSetting);
47
+
48
+ // Act
49
+ const createdSetting = await repository.create(newSetting);
50
+
51
+ // Assert
52
+ expect(createdSetting).toEqual(expectedSetting);
53
+ expect(repository.create).toHaveBeenCalledWith(newSetting);
54
+ });
55
+
56
+ it('should correctly handle updating an existing setting in the repository', async () => {
57
+ // Arrange
58
+ const repository = new SettingMongoRepository();
59
+ const existingSettingId = '1';
60
+ const updatedData: ISettingBase = {
61
+ key: 'existingKey',
62
+ value: 'updatedValue',
63
+ label: 'Updated Key Label',
64
+ group: 'Default Group',
65
+ type: 'string',
66
+ options: []
67
+ };
68
+ const expectedUpdatedSetting: ISetting = {
69
+ id: existingSettingId,
70
+ ...updatedData
71
+ };
72
+
73
+ // Mock the update method of the repository
74
+ vi.spyOn(repository, 'update').mockResolvedValue(expectedUpdatedSetting);
75
+
76
+ // Act
77
+ const updatedSetting = await repository.update(existingSettingId, updatedData);
78
+
79
+ // Assert
80
+ expect(updatedSetting).toEqual(expectedUpdatedSetting);
81
+ expect(repository.update).toHaveBeenCalledWith(existingSettingId, updatedData);
82
+ });
83
+
84
+
85
+ it('should correctly handle searching for a setting by _id', async () => {
86
+ // Arrange
87
+ const repository = new SettingMongoRepository();
88
+ const settingId = '1';
89
+ const expectedSetting: ISetting = {
90
+ id: settingId,
91
+ key: 'existingKey',
92
+ value: 'existingValue',
93
+ label: 'Existing Key Label',
94
+ group: 'Default Group',
95
+ type: 'string',
96
+ options: []
97
+ };
98
+
99
+ // Mock the findById method of the repository
100
+ vi.spyOn(repository, 'findById').mockResolvedValue(expectedSetting);
101
+
102
+ // Act
103
+ const foundSetting = await repository.findById(settingId);
104
+
105
+ // Assert
106
+ expect(foundSetting).toEqual(expectedSetting);
107
+ expect(repository.findById).toHaveBeenCalledWith(settingId);
108
+ });
109
+
110
+
111
+ it('should correctly handle searching for a setting by key', async () => {
112
+ // Arrange
113
+ const repository = new SettingMongoRepository();
114
+ const searchKey = 'sampleKey';
115
+ const expectedSettings: ISetting[] = [{
116
+ id: '1',
117
+ key: 'sampleKey',
118
+ value: 'sampleValue',
119
+ label: 'Sample Setting Label',
120
+ group: 'Sample Group',
121
+ type: 'string',
122
+ options: []
123
+ }];
124
+
125
+ // Mock the search method of the repository
126
+ vi.spyOn(repository, 'search').mockResolvedValue(expectedSettings);
127
+
128
+ // Act
129
+ const foundSettings = await repository.search(searchKey);
130
+
131
+ // Assert
132
+ expect(foundSettings).toEqual(expectedSettings);
133
+ expect(repository.search).toHaveBeenCalledWith(searchKey);
134
+ });
135
+
136
+
137
+
138
+ it('should correctly handle deleting a setting from the repository', async () => {
139
+ // Arrange
140
+ const repository = new SettingMongoRepository();
141
+ const settingId = '1';
142
+ const expectedDeletionResult = true;
143
+
144
+ // Mock the delete method of the repository
145
+ vi.spyOn(repository, 'delete').mockResolvedValue(expectedDeletionResult);
146
+
147
+ // Act
148
+ const deletionResult = await repository.delete(settingId);
149
+
150
+ // Assert
151
+ expect(deletionResult).toBe(expectedDeletionResult);
152
+ expect(repository.delete).toHaveBeenCalledWith(settingId);
153
+ });
154
+
155
+
156
+ it('should correctly handle retrieving all settings from the repository', async () => {
157
+ // Arrange
158
+ const repository = new SettingMongoRepository();
159
+ const expectedSettings: ISetting[] = [{
160
+ id: '1',
161
+ key: 'existingKey',
162
+ value: 'existingValue',
163
+ label: 'Existing Key Label',
164
+ group: 'Default Group',
165
+ type: 'string',
166
+ options: []
167
+ }];
168
+
169
+ // Mock the fetchAll method of the repository
170
+ vi.spyOn(repository, 'fetchAll').mockResolvedValue(expectedSettings);
171
+
172
+ // Act
173
+ const allSettings = await repository.fetchAll();
174
+
175
+ // Assert
176
+ expect(allSettings).toEqual(expectedSettings);
177
+ expect(repository.fetchAll).toHaveBeenCalled();
178
+ });
179
+
180
+ })