Browse Source

[PM-26671] Google workspace integration tests (#894)

Add tests for Google Workspace - not enabled in CI yet
pull/834/merge
Thomas Rittson 2 months ago committed by GitHub
parent
commit
fe01b49df1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 10
      .github/workflows/integration-test.yml
  2. 3
      .gitignore
  3. 4
      docker-compose.yml
  4. 10
      openldap/mkcert.sh
  5. 2
      package.json
  6. 2
      src/services/batch-requests-builder.spec.ts
  7. 82
      src/services/gsuite-directory.service.integration.spec.ts
  8. 10
      src/services/ldap-directory.service.integration.spec.ts
  9. 2
      src/services/single-request-builder.spec.ts
  10. 7
      src/services/sync.service.integration.spec.ts
  11. 6
      src/services/sync.service.spec.ts
  12. 4
      utils/.env.example
  13. 56
      utils/google-workspace/config-fixtures.ts
  14. 26
      utils/google-workspace/group-fixtures.ts
  15. 50
      utils/google-workspace/user-fixtures.ts
  16. 4
      utils/openldap/config-fixtures.ts
  17. 0
      utils/openldap/example-ldifs/directory-100.ldif
  18. 0
      utils/openldap/example-ldifs/directory-11000.ldif
  19. 0
      utils/openldap/example-ldifs/directory-250.ldif
  20. 0
      utils/openldap/example-ldifs/directory-50.ldif
  21. 0
      utils/openldap/example-ldifs/directory-500.ldif
  22. 2
      utils/openldap/group-fixtures.ts
  23. 0
      utils/openldap/ldifs/directory-20.ldif
  24. 10
      utils/openldap/mkcert.sh
  25. 2
      utils/openldap/user-fixtures.ts
  26. 4
      utils/request-builder-helper.ts

10
.github/workflows/integration-test.yml

@ -8,14 +8,14 @@ on: @@ -8,14 +8,14 @@ on:
paths:
- ".github/workflows/integration-test.yml" # this file
- "src/services/ldap-directory.service*" # we only have integration for LDAP testing at the moment
- "./openldap/**/*" # any change to test fixtures
- "./utils/**/*" # any change to test fixtures
- "./docker-compose.yml" # any change to Docker configuration
- "./package.json" # dependencies
pull_request:
paths:
- ".github/workflows/integration-test.yml" # this file
- "src/services/ldap-directory.service*" # we only have integration for LDAP testing at the moment
- "./openldap/**/*" # any change to test fixtures
- "./utils/**/*" # any change to test fixtures
- "./docker-compose.yml" # any change to Docker configuration
- "./package.json" # dependencies
permissions:
@ -55,11 +55,11 @@ jobs: @@ -55,11 +55,11 @@ jobs:
sudo apt-get update
sudo apt-get -y install mkcert
- name: Setup integration tests
- name: Setup LDAP integration tests
run: npm run test:integration:setup
- name: Run integration tests
run: npm run test:integration --coverage
- name: Run LDAP integration tests
run: npx jest ldap-directory.service.integration.spec.ts --coverage
- name: Report test results
uses: dorny/test-reporter@dc3a92680fcc15842eef52e8c4606ea7ce6bd3f3 # v2.1.1

3
.gitignore vendored

@ -2,6 +2,9 @@ @@ -2,6 +2,9 @@
.DS_Store
Thumbs.db
# Environment variables used for tests
.env
# IDEs and editors
.idea/
.project

4
docker-compose.yml

@ -11,8 +11,8 @@ services: @@ -11,8 +11,8 @@ services:
- LDAP_TLS_KEY_FILE=/certs/openldap-key.pem
- LDAP_TLS_CA_FILE=/certs/rootCA.pem
volumes:
- "./openldap/ldifs:/ldifs"
- "./openldap/certs:/certs"
- "./utils/openldap/ldifs:/ldifs"
- "./utils/openldap/certs:/certs"
ports:
- "1389:1389"
- "1636:1636"

10
openldap/mkcert.sh

@ -1,10 +0,0 @@ @@ -1,10 +0,0 @@
if ! [ -x "$(command -v mkcert)" ]; then
echo 'Error: mkcert is not installed. Install mkcert first and then re-run this script.'
echo 'e.g. brew install mkcert'
exit 1
fi
mkcert -install
mkdir -p ./openldap/certs
cp "$(mkcert -CAROOT)/rootCA.pem" ./openldap/certs/rootCA.pem
mkcert -key-file ./openldap/certs/openldap-key.pem -cert-file ./openldap/certs/openldap.pem localhost openldap

2
package.json

@ -69,7 +69,7 @@ @@ -69,7 +69,7 @@
"test:watch:all": "jest --watchAll --testPathIgnorePatterns=.integration.spec.ts",
"test:integration": "jest .integration.spec.ts",
"test:integration:watch": "jest .integration.spec.ts --watch",
"test:integration:setup": "sh ./openldap/mkcert.sh && docker compose up -d",
"test:integration:setup": "sh ./utils/openldap/mkcert.sh && docker compose up -d",
"test:types": "npx tsc --noEmit"
},
"devDependencies": {

2
src/services/batch-requests-builder.spec.ts

@ -2,8 +2,8 @@ import { GetUniqueString } from "@/jslib/common/spec/utils"; @@ -2,8 +2,8 @@ import { GetUniqueString } from "@/jslib/common/spec/utils";
import { UserEntry } from "@/src/models/userEntry";
import { groupSimulator, userSimulator } from "../../utils/request-builder-helper";
import { RequestBuilderOptions } from "../abstractions/request-builder.service";
import { groupSimulator, userSimulator } from "../utils/request-builder-helper";
import { BatchRequestBuilder } from "./batch-request-builder";

82
src/services/gsuite-directory.service.integration.spec.ts

@ -0,0 +1,82 @@ @@ -0,0 +1,82 @@
import { config as dotenvConfig } from "dotenv";
import { mock, MockProxy } from "jest-mock-extended";
import { I18nService } from "../../jslib/common/src/abstractions/i18n.service";
import { LogService } from "../../jslib/common/src/abstractions/log.service";
import {
getGSuiteConfiguration,
getSyncConfiguration,
} from "../../utils/google-workspace/config-fixtures";
import { groupFixtures } from "../../utils/google-workspace/group-fixtures";
import { userFixtures } from "../../utils/google-workspace/user-fixtures";
import { DirectoryType } from "../enums/directoryType";
import { GSuiteDirectoryService } from "./gsuite-directory.service";
import { StateService } from "./state.service";
// These tests integrate with a test Google Workspace instance.
// Credentials are located in the shared Bitwarden collection for Directory Connector testing.
// Place the .env file attachment in the utils folder.
// Load .env variables
dotenvConfig({ path: "utils/.env" });
// These filters target integration test data.
// These should return data that matches the user and group fixtures exactly.
// There may be additional data present if not used.
const INTEGRATION_USER_FILTER =
"exclude:integration-user-a@bwrox.dev|orgUnitPath='/Integration testing'";
const INTEGRATION_GROUP_FILTER = "|name:Integration*";
describe("gsuiteDirectoryService", () => {
let logService: MockProxy<LogService>;
let i18nService: MockProxy<I18nService>;
let stateService: MockProxy<StateService>;
let directoryService: GSuiteDirectoryService;
beforeEach(() => {
logService = mock();
i18nService = mock();
stateService = mock();
stateService.getDirectoryType.mockResolvedValue(DirectoryType.GSuite);
stateService.getLastUserSync.mockResolvedValue(null); // do not filter results by last modified date
i18nService.t.mockImplementation((id) => id); // passthrough implementation for any error messages
directoryService = new GSuiteDirectoryService(logService, i18nService, stateService);
});
it("syncs without using filters (includes test data)", async () => {
const directoryConfig = getGSuiteConfiguration();
stateService.getDirectory.calledWith(DirectoryType.GSuite).mockResolvedValue(directoryConfig);
const syncConfig = getSyncConfiguration({
groups: true,
users: true,
});
stateService.getSync.mockResolvedValue(syncConfig);
const result = await directoryService.getEntries(true, true);
expect(result[0]).toEqual(expect.arrayContaining(groupFixtures));
expect(result[1]).toEqual(expect.arrayContaining(userFixtures));
});
it("syncs using user and group filters (exact match for test data)", async () => {
const directoryConfig = getGSuiteConfiguration();
stateService.getDirectory.calledWith(DirectoryType.GSuite).mockResolvedValue(directoryConfig);
const syncConfig = getSyncConfiguration({
groups: true,
users: true,
userFilter: INTEGRATION_USER_FILTER,
groupFilter: INTEGRATION_GROUP_FILTER,
});
stateService.getSync.mockResolvedValue(syncConfig);
const result = await directoryService.getEntries(true, true);
expect(result).toEqual([groupFixtures, userFixtures]);
});
});

10
src/services/ldap-directory.service.integration.spec.ts

@ -2,10 +2,10 @@ import { mock, MockProxy } from "jest-mock-extended"; @@ -2,10 +2,10 @@ import { mock, MockProxy } from "jest-mock-extended";
import { I18nService } from "../../jslib/common/src/abstractions/i18n.service";
import { LogService } from "../../jslib/common/src/abstractions/log.service";
import { groupFixtures } from "../../openldap/group-fixtures";
import { userFixtures } from "../../openldap/user-fixtures";
import { getLdapConfiguration, getSyncConfiguration } from "../../utils/openldap/config-fixtures";
import { groupFixtures } from "../../utils/openldap/group-fixtures";
import { userFixtures } from "../../utils/openldap/user-fixtures";
import { DirectoryType } from "../enums/directoryType";
import { getLdapConfiguration, getSyncConfiguration } from "../utils/test-fixtures";
import { LdapDirectoryService } from "./ldap-directory.service";
import { StateService } from "./state.service";
@ -52,7 +52,7 @@ describe("ldapDirectoryService", () => { @@ -52,7 +52,7 @@ describe("ldapDirectoryService", () => {
getLdapConfiguration({
ssl: true,
startTls: true,
tlsCaPath: "./openldap/certs/rootCA.pem",
tlsCaPath: "./utils/openldap/certs/rootCA.pem",
}),
);
stateService.getSync.mockResolvedValue(getSyncConfiguration({ groups: true, users: true }));
@ -67,7 +67,7 @@ describe("ldapDirectoryService", () => { @@ -67,7 +67,7 @@ describe("ldapDirectoryService", () => {
getLdapConfiguration({
port: 1636,
ssl: true,
sslCaPath: "./openldap/certs/rootCA.pem",
sslCaPath: "./utils/openldap/certs/rootCA.pem",
}),
);
stateService.getSync.mockResolvedValue(getSyncConfiguration({ groups: true, users: true }));

2
src/services/single-request-builder.spec.ts

@ -2,8 +2,8 @@ import { GetUniqueString } from "@/jslib/common/spec/utils"; @@ -2,8 +2,8 @@ import { GetUniqueString } from "@/jslib/common/spec/utils";
import { UserEntry } from "@/src/models/userEntry";
import { groupSimulator, userSimulator } from "../../utils/request-builder-helper";
import { RequestBuilderOptions } from "../abstractions/request-builder.service";
import { groupSimulator, userSimulator } from "../utils/request-builder-helper";
import { SingleRequestBuilder } from "./single-request-builder";

7
src/services/sync.service.integration.spec.ts

@ -7,11 +7,9 @@ import { EnvironmentService } from "@/jslib/common/src/services/environment.serv @@ -7,11 +7,9 @@ import { EnvironmentService } from "@/jslib/common/src/services/environment.serv
import { I18nService } from "../../jslib/common/src/abstractions/i18n.service";
import { LogService } from "../../jslib/common/src/abstractions/log.service";
import { groupFixtures } from "../../openldap/group-fixtures";
import { userFixtures } from "../../openldap/user-fixtures";
import { getLdapConfiguration, getSyncConfiguration } from "../../utils/openldap/config-fixtures";
import { DirectoryFactoryService } from "../abstractions/directory-factory.service";
import { DirectoryType } from "../enums/directoryType";
import { getLdapConfiguration, getSyncConfiguration } from "../utils/test-fixtures";
import { BatchRequestBuilder } from "./batch-request-builder";
import { LdapDirectoryService } from "./ldap-directory.service";
@ -20,6 +18,9 @@ import { StateService } from "./state.service"; @@ -20,6 +18,9 @@ import { StateService } from "./state.service";
import { SyncService } from "./sync.service";
import * as constants from "./sync.service";
import { groupFixtures } from "@/utils/openldap/group-fixtures";
import { userFixtures } from "@/utils/openldap/user-fixtures";
describe("SyncService", () => {
let logService: MockProxy<LogService>;
let i18nService: MockProxy<I18nService>;

6
src/services/sync.service.spec.ts

@ -6,9 +6,9 @@ import { MessagingService } from "@/jslib/common/src/abstractions/messaging.serv @@ -6,9 +6,9 @@ import { MessagingService } from "@/jslib/common/src/abstractions/messaging.serv
import { OrganizationImportRequest } from "@/jslib/common/src/models/request/organizationImportRequest";
import { ApiService } from "@/jslib/common/src/services/api.service";
import { getSyncConfiguration } from "../../utils/openldap/config-fixtures";
import { DirectoryFactoryService } from "../abstractions/directory-factory.service";
import { DirectoryType } from "../enums/directoryType";
import { getSyncConfiguration } from "../utils/test-fixtures";
import { BatchRequestBuilder } from "./batch-request-builder";
import { I18nService } from "./i18n.service";
@ -18,8 +18,8 @@ import { StateService } from "./state.service"; @@ -18,8 +18,8 @@ import { StateService } from "./state.service";
import { SyncService } from "./sync.service";
import * as constants from "./sync.service";
import { groupFixtures } from "@/openldap/group-fixtures";
import { userFixtures } from "@/openldap/user-fixtures";
import { groupFixtures } from "@/utils/openldap/group-fixtures";
import { userFixtures } from "@/utils/openldap/user-fixtures";
describe("SyncService", () => {
let cryptoFunctionService: MockProxy<CryptoFunctionService>;

4
utils/.env.example

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
GOOGLE_DOMAIN=
GOOGLE_ADMIN_USER=
GOOGLE_CLIENT_EMAIL=
GOOGLE_PRIVATE_KEY=

56
utils/google-workspace/config-fixtures.ts

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
import { GSuiteConfiguration } from "../../src/models/gsuiteConfiguration";
import { SyncConfiguration } from "../../src/models/syncConfiguration";
/**
* @returns a basic GSuite configuration. Can be overridden by passing in a partial configuration.
*/
export const getGSuiteConfiguration = (
config?: Partial<GSuiteConfiguration>,
): GSuiteConfiguration => {
const adminUser = process.env.GOOGLE_ADMIN_USER;
const clientEmail = process.env.GOOGLE_CLIENT_EMAIL;
const privateKey = process.env.GOOGLE_PRIVATE_KEY;
const domain = process.env.GOOGLE_DOMAIN;
if (!adminUser || !clientEmail || !privateKey || !domain) {
throw new Error("Google Workspace integration test credentials not configured.");
}
return {
// TODO
adminUser,
clientEmail,
privateKey,
domain: domain,
customer: "",
...(config ?? {}),
};
};
/**
* @returns a basic Google Workspace sync configuration. Can be overridden by passing in a partial configuration.
*/
export const getSyncConfiguration = (config?: Partial<SyncConfiguration>): SyncConfiguration => ({
users: false,
groups: false,
interval: 5,
userFilter: "",
groupFilter: "",
removeDisabled: false,
overwriteExisting: false,
largeImport: false,
// Ldap properties - not optional for some reason
groupObjectClass: "",
userObjectClass: "",
groupPath: null,
userPath: null,
groupNameAttribute: "",
userEmailAttribute: "",
memberAttribute: "",
useEmailPrefixSuffix: false,
emailPrefixAttribute: "",
emailSuffix: null,
creationDateAttribute: "",
revisionDateAttribute: "",
...(config ?? {}),
});

26
utils/google-workspace/group-fixtures.ts

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
import { Jsonify } from "type-fest";
import { GroupEntry } from "../../src/models/groupEntry";
// These must match the Google Workspace seed data
const data: Jsonify<GroupEntry>[] = [
{
externalId: "0319y80a3anpxhj",
groupMemberReferenceIds: [],
name: "Integration Test Group A",
referenceId: "0319y80a3anpxhj",
userMemberExternalIds: ["111605910541641314041", "111147009830456099026"],
users: [],
},
{
externalId: "02afmg28317uyub",
groupMemberReferenceIds: [],
name: "Integration Test Group B",
referenceId: "02afmg28317uyub",
userMemberExternalIds: ["111147009830456099026", "100150970267699397306"],
users: [],
},
];
export const groupFixtures = data.map((g) => GroupEntry.fromJSON(g));

50
utils/google-workspace/user-fixtures.ts

@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
import { Jsonify } from "type-fest";
import { UserEntry } from "../../src/models/userEntry";
// These must match the Google Workspace seed data
const data: Jsonify<UserEntry>[] = [
// In Group A
{
deleted: false,
disabled: false,
email: "testuser1@bwrox.dev",
externalId: "111605910541641314041",
referenceId: "111605910541641314041",
},
// In Groups A + B
{
deleted: false,
disabled: false,
email: "testuser2@bwrox.dev",
externalId: "111147009830456099026",
referenceId: "111147009830456099026",
},
// In Group B
{
deleted: false,
disabled: false,
email: "testuser3@bwrox.dev",
externalId: "100150970267699397306",
referenceId: "100150970267699397306",
},
// Not in a group
{
deleted: false,
disabled: false,
email: "testuser4@bwrox.dev",
externalId: "113764752650306721470",
referenceId: "113764752650306721470",
},
// Disabled user
{
deleted: false,
disabled: true,
email: "testuser5@bwrox.dev",
externalId: "110381976819725658200",
referenceId: "110381976819725658200",
},
];
export const userFixtures = data.map((g) => UserEntry.fromJSON(g));

4
src/utils/test-fixtures.ts → utils/openldap/config-fixtures.ts

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
import { LdapConfiguration } from "../models/ldapConfiguration";
import { SyncConfiguration } from "../models/syncConfiguration";
import { LdapConfiguration } from "../../src/models/ldapConfiguration";
import { SyncConfiguration } from "../../src/models/syncConfiguration";
/**
* @returns a basic ldap configuration without TLS/SSL enabled. Can be overridden by passing in a partial configuration.

0
openldap/example-ldifs/directory-100.ldif → utils/openldap/example-ldifs/directory-100.ldif

0
openldap/example-ldifs/directory-11000.ldif → utils/openldap/example-ldifs/directory-11000.ldif

0
openldap/example-ldifs/directory-250.ldif → utils/openldap/example-ldifs/directory-250.ldif

0
openldap/example-ldifs/directory-50.ldif → utils/openldap/example-ldifs/directory-50.ldif

0
openldap/example-ldifs/directory-500.ldif → utils/openldap/example-ldifs/directory-500.ldif

2
openldap/group-fixtures.ts → utils/openldap/group-fixtures.ts

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
import { Jsonify } from "type-fest";
import { GroupEntry } from "../src/models/groupEntry";
import { GroupEntry } from "@/src/models/groupEntry";
// These must match the ldap server seed data in directory.ldif
const data: Jsonify<GroupEntry>[] = [

0
openldap/ldifs/directory-20.ldif → utils/openldap/ldifs/directory-20.ldif

10
utils/openldap/mkcert.sh

@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
if ! [ -x "$(command -v mkcert)" ]; then
echo 'Error: mkcert is not installed. Install mkcert first and then re-run this script.'
echo 'e.g. brew install mkcert'
exit 1
fi
mkcert -install
mkdir -p ./utils/openldap/certs
cp "$(mkcert -CAROOT)/rootCA.pem" ./utils/openldap/certs/rootCA.pem
mkcert -key-file ./utils/openldap/certs/openldap-key.pem -cert-file ./utils/openldap/certs/openldap.pem localhost openldap

2
openldap/user-fixtures.ts → utils/openldap/user-fixtures.ts

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
import { Jsonify } from "type-fest";
import { UserEntry } from "../src/models/userEntry";
import { UserEntry } from "@/src/models/userEntry";
// These must match the ldap server seed data in directory.ldif
const data: Jsonify<UserEntry>[] = [

4
src/utils/request-builder-helper.ts → utils/request-builder-helper.ts

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
import { GetUniqueString } from "@/jslib/common/spec/utils";
import { GroupEntry } from "../models/groupEntry";
import { UserEntry } from "../models/userEntry";
import { GroupEntry } from "../src/models/groupEntry";
import { UserEntry } from "../src/models/userEntry";
export function userSimulator(userCount: number): UserEntry[] {
const users: UserEntry[] = [];
Loading…
Cancel
Save