Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,38 @@ class Configuration extends BaseModel {
return false;
}

// Get configuration for a sandbox audit type
getSandboxAuditConfig(auditType) {
return this.getSandboxAudits()?.[auditType] || null;
}

// Get all enabled sandbox audit types
getEnabledSandboxAudits() {
return Object.keys(this.getSandboxAudits() || {});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the list be filtered by enabled true, according to the schema https://github.com/adobe/spacecat-shared/pull/941/files#diff-ec6a8162056224108b6fc55e86bf6d58298ed01c9cd75a0bb024369f6356b49bR52 ?
Same for isAuditEnabledForSandbox below.

}

// Check if a specific audit type is enabled for sandbox
isAuditEnabledForSandbox(auditType) {
return this.getSandboxAudits()?.[auditType] !== undefined;
}

// Check if this configuration has any sandbox audits configured
hasSandboxAudits() {
const sandboxAudits = this.getSandboxAudits();
return !!(sandboxAudits && Object.keys(sandboxAudits || {}).length > 0);
}

// Update sandbox audit configuration
// This method updates the sandbox audit configuration for a specific audit type
updateSandboxAuditConfig(auditType, config) {
const currentSandboxAudits = this.getSandboxAudits() || {};
const updatedSandboxAudits = {
...currentSandboxAudits,
[auditType]: config,
};
this.setSandboxAudits(updatedSandboxAudits);
}

isHandlerEnabledForOrg(type, org) {
const handler = this.getHandlers()?.[type];
if (!handler) return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,27 @@ const jobsSchema = Joi.array().required();

const queueSchema = Joi.object().required();

// Schema for individual sandbox audit configuration - simple and flexible
const sandboxAuditConfigSchema = Joi.object({
enabled: Joi.boolean().required(), // Required: whether audit is enabled
cooldownHours: Joi.alternatives().try(
Joi.string(), // Accept string like "3", "10"
Joi.number().min(0), // Accept number like 3, 10
).required(), // Required: cooldown time in hours before next audit allowed
}).unknown(true); // Allow additional properties for future extensibility

// Schema for the entire sandboxAudits object
const sandboxAuditsSchema = Joi.object().pattern(
Joi.string().pattern(/^[a-zA-Z0-9_-]+$/), // Audit type names: alphanumeric, hyphens, underscores
sandboxAuditConfigSchema,
).optional();

const configurationSchema = Joi.object({
version: Joi.number().required(),
queues: queueSchema,
handlers: handlerSchema,
jobs: jobsSchema,
sandboxAudits: sandboxAuditsSchema,
}).unknown(true);

export const checkConfiguration = (data, schema = configurationSchema) => {
Expand Down Expand Up @@ -95,6 +111,17 @@ const schema = new SchemaBuilder(Configuration, ConfigurationCollection)
type: 'any',
validate: (value) => !value || isNonEmptyObject(value),
})
.addAttribute('sandboxAudits', {
type: 'any',
validate: (value) => {
// Use Joi schema validation
const { error } = sandboxAuditsSchema.validate(value);
if (error) {
throw new Error(`Sandbox audits validation error: ${error.message}`);
}
return true;
},
})
.addAttribute('version', {
type: 'number',
required: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,149 @@ describe('ConfigurationModel', () => {
});
});

describe('sandbox audit configuration', () => {
beforeEach(() => {
// Add sandbox audit config to the test configuration
instance.state = {
version: 1,
queues: { test: 'test' },
jobs: [],
handlers: {},
};
// Set sandboxAudits using the proper attribute setter
instance.setSandboxAudits({
cwv: {
cooldownHours: '5',
},
'alt-text': {},
});
});

it('checks if audit is enabled for sandbox', () => {
expect(instance.isAuditEnabledForSandbox('cwv')).to.be.true;
expect(instance.isAuditEnabledForSandbox('alt-text')).to.be.true;
expect(instance.isAuditEnabledForSandbox('non-existent')).to.be.false;
});

it('gets sandbox audit config', () => {
expect(instance.getSandboxAuditConfig('cwv')).to.deep.equal({ cooldownHours: '5' });
expect(instance.getSandboxAuditConfig('alt-text')).to.deep.equal({});
expect(instance.getSandboxAuditConfig('non-existent')).to.be.null;
});

it('gets all enabled sandbox audits', () => {
expect(instance.getEnabledSandboxAudits()).to.deep.equal(['cwv', 'alt-text']);
});

it('adds new sandbox audit config', () => {
instance.updateSandboxAuditConfig('new-audit', { cooldownHours: '5' });
expect(instance.getSandboxAuditConfig('new-audit')).to.deep.equal({ cooldownHours: '5' });
});

it('updates existing sandbox audit config', () => {
instance.updateSandboxAuditConfig('cwv', { cooldownHours: '10', enabled: false });
expect(instance.getSandboxAuditConfig('cwv')).to.deep.equal({ cooldownHours: '10', enabled: false });
});

it('returns empty array when no sandbox audits configured', () => {
instance.setSandboxAudits(null);
expect(instance.getEnabledSandboxAudits()).to.deep.equal([]);
});

it('handles updating non-existent sandbox config', () => {
instance.setSandboxAudits(null);
instance.updateSandboxAuditConfig('new-audit', { cooldownHours: '5' });
expect(instance.getSandboxAuditConfig('new-audit')).to.deep.equal({ cooldownHours: '5' });
});

it('handles getSandboxAuditConfig when sandboxAudits is null', () => {
instance.setSandboxAudits(null);
expect(instance.getSandboxAuditConfig('any-audit')).to.be.null;
});

it('handles getSandboxAuditConfig when sandboxAudits is undefined', () => {
instance.setSandboxAudits(undefined);
expect(instance.getSandboxAuditConfig('any-audit')).to.be.null;
});

it('handles getSandboxAuditConfig when sandboxAudits is null', () => {
instance.setSandboxAudits(null);
expect(instance.getSandboxAuditConfig('any-audit')).to.be.null;
});

it('handles getEnabledSandboxAudits when sandboxAudits is null', () => {
instance.setSandboxAudits(null);
expect(instance.getEnabledSandboxAudits()).to.deep.equal([]);
});

it('handles getEnabledSandboxAudits when sandboxAudits is undefined', () => {
instance.setSandboxAudits(undefined);
expect(instance.getEnabledSandboxAudits()).to.deep.equal([]);
});

it('handles getEnabledSandboxAudits when sandboxAudits is empty', () => {
instance.setSandboxAudits({});
expect(instance.getEnabledSandboxAudits()).to.deep.equal([]);
});

it('handles isAuditEnabledForSandbox when sandboxAudits is null', () => {
instance.setSandboxAudits(null);
expect(instance.isAuditEnabledForSandbox('any-audit')).to.be.false;
});

it('handles isAuditEnabledForSandbox when sandboxAudits is undefined', () => {
instance.setSandboxAudits(undefined);
expect(instance.isAuditEnabledForSandbox('any-audit')).to.be.false;
});

it('handles isAuditEnabledForSandbox when sandboxAudits is empty', () => {
instance.setSandboxAudits({});
expect(instance.isAuditEnabledForSandbox('any-audit')).to.be.false;
});

it('checks if configuration has sandbox audits', () => {
// Should return true when sandbox audits are configured
expect(instance.hasSandboxAudits()).to.be.true;

// Should return false when sandbox audits are null
instance.setSandboxAudits(null);
expect(instance.hasSandboxAudits()).to.be.false;

// Should return false when sandbox audits are undefined
instance.setSandboxAudits(undefined);
expect(instance.hasSandboxAudits()).to.be.false;

// Should return false when sandboxAudits is empty
instance.setSandboxAudits({});
expect(instance.hasSandboxAudits()).to.be.false;

// Should return false when sandboxAudits is null
instance.setSandboxAudits(null);
expect(instance.hasSandboxAudits()).to.be.false;

// Should return true when sandboxAudits has content
instance.setSandboxAudits({ cwv: {} });
expect(instance.hasSandboxAudits()).to.be.true;
});
});

describe('Handler Management', () => {
it('handles disableHandlerForSite when handler is not enabled', () => {
const mockSite = {
getId: () => 'site2',
getOrganizationId: () => 'org2',
};
// Should not throw error when trying to disable non-enabled handler
expect(() => instance.disableHandlerForSite('cwv', mockSite)).to.not.throw();
});

it('handles disableHandlerForOrg when handler is not enabled', () => {
const mockOrg = { getId: () => 'org2' };
// Should not throw error when trying to disable non-enabled handler
expect(() => instance.disableHandlerForOrg('cwv', mockOrg)).to.not.throw();
});
});

describe('save', () => {
it('saves the configuration', async () => {
instance.collection = {
Expand Down