diff --git a/src/pepr/operator/controllers/keycloak/authservice/authservice.spec.ts b/src/pepr/operator/controllers/keycloak/authservice/authservice.spec.ts index 0ed1ca897..9a814c46e 100644 --- a/src/pepr/operator/controllers/keycloak/authservice/authservice.spec.ts +++ b/src/pepr/operator/controllers/keycloak/authservice/authservice.spec.ts @@ -126,4 +126,157 @@ describe("authservice", () => { expect(e).toBeUndefined(); } }); + + test("should add multiple chains to authservice", async () => { + let config = buildConfig(mockConfig as AuthserviceConfig, { + client: mockClient, + name: "local", + action: Action.Add, + }); + + config = buildConfig(config, { + client: mockClient, + name: "some-other-name", + action: Action.Add, + }); + config = buildConfig(config, { + client: mockClient, + name: "some-second-name", + action: Action.Add, + }); + config = buildConfig(config, { + client: mockClient, + name: "some-third-name", + action: Action.Add, + }); + + expect(config.chains.length).toEqual(4); + }); + + test("should add multiple chains to authservice and be sorted", async () => { + let config1 = buildConfig(mockConfig as AuthserviceConfig, { + client: mockClient, + name: "local", + action: Action.Remove, + }); + + // after sorting, the order should be like so: + // const unsortedNames = [ + // "first-name" + // "some-fifth-name", + // "some-other-name", + // "some-second-name", + // "some-third-name", + // ] + + const unsortedNames = [ + "some-other-name", + "first-name", + "some-third-name", + "some-second-name", + "some-fifth-name", + ]; + shuffleArray(unsortedNames); + unsortedNames.map(val => { + config1 = buildConfig(config1, { + client: mockClient, + name: val, + action: Action.Add, + }); + }); + + expect(config1.chains.length).toEqual(5); + expect(config1.chains[0].name).toEqual("first-name"); + }); + + test("should add multiple chains to authservice and be sorted, with removals", async () => { + let config1 = buildConfig(mockConfig as AuthserviceConfig, { + client: mockClient, + name: "local", + action: Action.Remove, + }); + + const unsortedNames = [ + "some-other-name", + "first-name", + "some-second-name", + "some-third-name", + "some-fifth-name", + ]; + shuffleArray(unsortedNames); + unsortedNames.map(val => { + config1 = buildConfig(config1, { + client: mockClient, + name: val, + action: Action.Add, + }); + }); + + expect(config1.chains.length).toEqual(5); + expect(config1.chains[0].name).toEqual("first-name"); + expect(config1.chains[4].name).toEqual("some-third-name"); + + config1 = buildConfig(config1, { + client: mockClient, + name: "some-third-name", + action: Action.Remove, + }); + + expect(config1.chains.length).toEqual(4); + expect(config1.chains[0].name).toEqual("first-name"); + expect(config1.chains[3].name).toEqual("some-second-name"); + + config1 = buildConfig(config1, { + client: mockClient, + name: "some-fifth-name", + action: Action.Remove, + }); + + expect(config1.chains.length).toEqual(3); + expect(config1.chains[0].name).toEqual("first-name"); + expect(config1.chains[2].name).toEqual("some-second-name"); + + config1 = buildConfig(config1, { + client: mockClient, + name: "aaaa-final", + action: Action.Add, + }); + expect(config1.chains.length).toEqual(4); + expect(config1.chains[0].name).toEqual("aaaa-final"); + + config1 = buildConfig(config1, { + client: mockClient, + name: "1-something", + action: Action.Add, + }); + + config1 = buildConfig(config1, { + client: mockClient, + name: "10-something", + action: Action.Add, + }); + + config1 = buildConfig(config1, { + client: mockClient, + name: "2-something", + action: Action.Add, + }); + expect(config1.chains.length).toEqual(7); + expect(config1.chains[0].name).toEqual("1-something"); + expect(config1.chains[1].name).toEqual("10-something"); + expect(config1.chains[2].name).toEqual("2-something"); + }); }); + +/** + * Randomize array in-place using Durstenfeld shuffle algorithm + * ripped this from some source on the internet + * */ +function shuffleArray(array: string[]) { + for (let i = array.length - 1; i >= 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + const temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } +} diff --git a/src/pepr/operator/controllers/keycloak/authservice/authservice.ts b/src/pepr/operator/controllers/keycloak/authservice/authservice.ts index 0d6af015d..52a880cb8 100644 --- a/src/pepr/operator/controllers/keycloak/authservice/authservice.ts +++ b/src/pepr/operator/controllers/keycloak/authservice/authservice.ts @@ -112,8 +112,13 @@ export function buildConfig(config: AuthserviceConfig, event: AuthServiceEvent) // Add the new chain to the existing authservice config chains = config.chains.filter(chain => chain.name !== event.name); chains = chains.concat(buildChain(event)); + // Sort the chains by their name before returning. Note that the accuracy of + // sorting here is not relevant, only the consistency. + const sortByName = R.sortBy(R.prop("name")); + chains = sortByName(chains); } else if (event.action == Action.Remove) { - // Search in the existing chains for the chain to remove by name + // Search in the existing chains for the chain to remove by name. + // Filtering here should preserve the order, so there is no need to re-sort. chains = config.chains.filter(chain => chain.name !== event.name); } else { throw new Error(`Unhandled Action: ${event.action satisfies never}`);