101 lines
3.1 KiB
TypeScript
101 lines
3.1 KiB
TypeScript
import { describe, test, expect, beforeEach } from '@jest/globals';
|
|
import { RhizomeNode, Lossless, createDelta } from '@src';
|
|
import { CollapsedDelta } from '@src/views/lossless';
|
|
import { CustomResolver, ResolverPlugin } from '@src/views/resolvers/custom-resolvers';
|
|
import type { DependencyStates } from '@src/views/resolvers/custom-resolvers';
|
|
|
|
type PropertyTypes = string | number | boolean | null;
|
|
|
|
describe('Circular Dependency Detection', () => {
|
|
let node: RhizomeNode;
|
|
let lossless: Lossless;
|
|
|
|
beforeEach(() => {
|
|
node = new RhizomeNode();
|
|
lossless = new Lossless(node);
|
|
});
|
|
|
|
test('should detect circular dependencies', () => {
|
|
// PluginA depends on PluginB
|
|
class PluginA implements ResolverPlugin<{ value: string }, string> {
|
|
readonly name = 'a' as const;
|
|
readonly dependencies = ['b'] as const;
|
|
|
|
initialize() {
|
|
return { value: '' };
|
|
}
|
|
|
|
update(currentState: { value: string }, newValue: PropertyTypes, _delta: CollapsedDelta, _dependencies: { b: string }) {
|
|
return { value: String(newValue) };
|
|
}
|
|
|
|
resolve(_state: { value: string }, _dependencies: { b: string }) {
|
|
return 'a';
|
|
}
|
|
}
|
|
|
|
|
|
// PluginB depends on PluginA (circular dependency)
|
|
class PluginB implements ResolverPlugin<{ value: string }, string> {
|
|
readonly name = 'b' as const;
|
|
readonly dependencies = ['a'] as const;
|
|
|
|
initialize() {
|
|
return { value: '' };
|
|
}
|
|
|
|
update(currentState: { value: string }, newValue: PropertyTypes, _delta: CollapsedDelta, _dependencies: { a: string }) {
|
|
return { value: String(newValue) };
|
|
}
|
|
|
|
resolve(_state: { value: string }, _dependencies: { a: string }) {
|
|
return 'b';
|
|
}
|
|
}
|
|
|
|
|
|
// Should throw an error when circular dependencies are detected
|
|
expect(() => {
|
|
new CustomResolver(lossless, {
|
|
'a': new PluginA(),
|
|
'b': new PluginB()
|
|
});
|
|
}).toThrow('Circular dependency detected: a -> b -> a');
|
|
});
|
|
|
|
test('should detect longer circular dependency chains', () => {
|
|
class PluginA implements ResolverPlugin<{ value: string }, string> {
|
|
readonly name = 'a' as const;
|
|
readonly dependencies = ['c'] as const;
|
|
initialize() { return { value: '' }; }
|
|
update() { return { value: '' }; }
|
|
resolve() { return 'a'; }
|
|
}
|
|
|
|
class PluginB implements ResolverPlugin<{ value: string }, string> {
|
|
readonly name = 'b' as const;
|
|
readonly dependencies = ['a'] as const;
|
|
initialize() { return { value: '' }; }
|
|
update() { return { value: '' }; }
|
|
resolve() { return 'b'; }
|
|
}
|
|
|
|
class PluginC implements ResolverPlugin<{ value: string }, string> {
|
|
readonly name = 'c' as const;
|
|
readonly dependencies = ['b'] as const;
|
|
initialize() { return { value: '' }; }
|
|
update() { return { value: '' }; }
|
|
resolve() { return 'c'; }
|
|
}
|
|
|
|
// Should detect the circular dependency: a -> c -> b -> a
|
|
expect(() => {
|
|
new CustomResolver(lossless, {
|
|
'a': new PluginA(),
|
|
'b': new PluginB(),
|
|
'c': new PluginC()
|
|
});
|
|
}).toThrow('Circular dependency detected: a -> c -> b -> a');
|
|
});
|
|
});
|