integration

This commit is contained in:
Elio Bischof
2025-06-08 02:08:29 +02:00
parent 5fa1e7329b
commit cdc421e0d0
19 changed files with 122 additions and 157 deletions

View File

@@ -2,8 +2,6 @@ LOGIN_DEPENDENCIES_TAG ?= "zitadel-login-dependencies:local"
LOGIN_IMAGE_TAG ?= "zitadel-login:local" LOGIN_IMAGE_TAG ?= "zitadel-login:local"
CORE_MOCK_TAG ?= "zitadel-core-mock:local" CORE_MOCK_TAG ?= "zitadel-core-mock:local"
LOGIN_INTEGRATION_TESTSUITE_TAG ?= "zitadel-login-integration-testsuite:local" LOGIN_INTEGRATION_TESTSUITE_TAG ?= "zitadel-login-integration-testsuite:local"
CORE_MOCK_CONTAINER_NAME ?= zitadel-mock-grpc-server
LOGIN_CONTAINER_NAME ?= zitadel-login
XDG_CACHE_HOME ?= $(HOME)/.cache XDG_CACHE_HOME ?= $(HOME)/.cache
export CACHE_DIR ?= $(XDG_CACHE_HOME)/zitadel-make export CACHE_DIR ?= $(XDG_CACHE_HOME)/zitadel-make
@@ -12,21 +10,18 @@ export CACHE_DIR ?= $(XDG_CACHE_HOME)/zitadel-make
help: help:
@echo "Makefile for the login service" @echo "Makefile for the login service"
@echo "Available targets:" @echo "Available targets:"
@echo " help - Show this help message" @echo " help - Show this help message."
@echo " login - Start the login service" @echo " login-lint - Run linting and formatting checks. FORCE=true prevents skipping."
@echo " login-lint - Run linting and formatting checks" @echo " login-lint-force - Force run linting and formatting checks."
@echo " login-lint-force - Force run linting and formatting checks" @echo " login-unit - Run unit tests. FORCE=true prevents skipping."
@echo " login-unit - Run unit tests" @echo " login-unit-force - Force run unit tests."
@echo " login-unit-force - Force run unit tests" @echo " login-integration - Run integration tests. FORCE=true prevents skipping."
@echo " login-integration - Run integration tests" @echo " login-integration-force - Force run integration tests."
@echo " login-integration-force - Force run integration tests" @echo " login-standalone-build - Build the docker image for production login containers."
@echo " login-standalone - Build the docker image for production login containers" @echo " login-quality - Run all quality checks (login-lint, unit, integration)."
@echo " login-quality - Run all quality checks (login-lint, unit, integration)" @echo " login-ci - Run all CI tasks. Run it with the -j flag to parallelize: make -j ci."
@echo " login-ci - Run all CI tasks. Run it with the -j flag to parallelize. make -j ci" @echo " show-cache-keys - Show all cache keys with image ids and exit codes."
@echo " show-cache-keys - Show all cache keys with image ids and exit codes" @echo " clean-cache-keys - Remove all cache keys."
@echo " clean-cache-keys - Remove all cache keys"
@echo " core-mock - Start the core mock server"
@echo " core-mock-stop - Stop the core mock server"
.PHONY: login-lint-force .PHONY: login-lint-force
@@ -47,13 +42,12 @@ login-unit:
./scripts/run_or_skip.sh login-unit-force $(LOGIN_DEPENDENCIES_TAG) ./scripts/run_or_skip.sh login-unit-force $(LOGIN_DEPENDENCIES_TAG)
.PHONY: login-integration-force .PHONY: login-integration-force
login-integration-force: login core-mock login-integration-testsuite login-integration-force: login-standalone-build core-mock-build login-integration-testsuite-build
docker run --rm $(LOGIN_INTEGRATION_TESTSUITE_TAG) docker compose --file ./apps/login-integration-testsuite/docker-compose.yaml run --rm integration-testsuite
$(MAKE) core-mock-stop
.PHONY: login-integration .PHONY: login-integration
login-integration: login-integration:
./scripts/run_or_skip.sh login-integration-force '$(LOGIN_DEPENDENCIES_TAG);$(CORE_MOCK_TAG);$(LOGIN_INTEGRATION_TESTSUITE_TAG)' ./scripts/run_or_skip.sh login-integration-force '$(LOGIN_IMAGE_TAG);$(LOGIN_INTEGRATION_TESTSUITE_TAG);$(CORE_MOCK_TAG)'
.PHONY: login-quality .PHONY: login-quality
login-quality: core-mock-build login-quality-after-build login-quality: core-mock-build login-quality-after-build
@@ -62,37 +56,22 @@ login-quality-after-build: login-lint login-unit login-integration
.PHONY: login-ci .PHONY: login-ci
login-ci: core-mock-build login-ci-after-build login-ci: core-mock-build login-ci-after-build
login-ci-after-build: login-quality-after-build login-standalone login-ci-after-build: login-quality-after-build login-standalone-build
@: @:
login-dependencies: login-dependencies:
docker buildx bake login-dependencies --set login-dependencies.tags=$(LOGIN_DEPENDENCIES_TAG); docker buildx bake login-dependencies --set login-dependencies.tags=$(LOGIN_DEPENDENCIES_TAG);
.PHONY: login-standalone .PHONY: login-standalone-build
login-standalone: login-standalone-build:
docker buildx bake login-standalone --set login-standalone.tags=$(LOGIN_IMAGE_TAG); docker buildx bake login-standalone --set login-standalone.tags=$(LOGIN_IMAGE_TAG);
.PHONY: login
login: login-standalone login-stop
docker run --detach --rm --name $(LOGIN_CONTAINER_NAME) --publish 3000:3000 $(LOGIN_IMAGE_TAG)
login-stop:
docker rm --force $(LOGIN_CONTAINER_NAME) 2>/dev/null || true
core-mock-build: core-mock-build:
docker buildx bake core-mock --set core-mock.tags=$(CORE_MOCK_TAG); docker buildx bake core-mock --set core-mock.tags=$(CORE_MOCK_TAG);
login-integration-testsuite: login-dependencies login-integration-testsuite-build: login-dependencies
docker buildx bake login-integration-testsuite --set login-integration-testsuite.tags=$(LOGIN_INTEGRATION_TESTSUITE_TAG) docker buildx bake login-integration-testsuite --set login-integration-testsuite.tags=$(LOGIN_INTEGRATION_TESTSUITE_TAG)
.PHONY: core-mock
core-mock: core-mock-build core-mock-stop
docker run --detach --rm --name $(CORE_MOCK_CONTAINER_NAME) --publish 22221:22221 --publish 22222:22222 $(CORE_MOCK_TAG)
.PHONY: core-mock-stop
core-mock-stop:
docker rm --force $(CORE_MOCK_CONTAINER_NAME) 2>/dev/null || true
.PHONY: clean-cache-keys .PHONY: clean-cache-keys
clean-cache-keys: clean-cache-keys:
@echo "Removing cache directory: $(CACHE_DIR)" @echo "Removing cache directory: $(CACHE_DIR)"

View File

@@ -6,4 +6,4 @@ COPY mocked-services.cfg .
COPY initial-stubs initial-stubs COPY initial-stubs initial-stubs
COPY --from=protos . . COPY --from=protos . .
ENTRYPOINT [ "sh", "-c", "grpc-mock -v 1 -protos $(tr '\n' ',' < ./mocked-services.cfg) -stub-dir ./initial-stubs" ] ENTRYPOINT [ "sh", "-c", "grpc-mock -v 1 -proto $(tr '\n' ',' < ./mocked-services.cfg) -stub-dir ./initial-stubs" ]

View File

@@ -2,9 +2,11 @@ import { defineConfig } from "cypress";
export default defineConfig({ export default defineConfig({
reporter: "list", reporter: "list",
e2e: { e2e: {
baseUrl: "http://localhost:3000", baseUrl: process.env.LOGIN_BASE_URL || "http://localhost:3000",
specPattern: "integration/**/*.cy.{js,jsx,ts,tsx}", specPattern: "integration/**/*.cy.{js,jsx,ts,tsx}",
supportFile: "support/e2e.{js,jsx,ts,tsx}",
setupNodeEvents(on, config) { setupNodeEvents(on, config) {
// implement node event listeners here // implement node event listeners here
}, },

View File

@@ -0,0 +1,31 @@
services:
core-mock:
image: ${CORE_MOCK_TAG:-zitadel-core-mock:local}
container_name: core-mock
ports:
- 22220:22220
- 22222:22222
login:
image: ${LOGIN_IMAGE_TAG:-zitadel-login:local}
container_name: login
ports:
- 3000:3000
environment:
- ZITADEL_API_URL=http://core-mock:22222
- ZITADEL_SERVICE_USER_TOKEN="yolo"
- EMAIL_VERIFICATION=true
- DEBUG=true
- NEXT_PUBLIC_BASE_PATH=""
integration-testsuite:
image: ${LOGIN_INTEGRATION_TESTSUITE_TAG:-zitadel-login-integration-testsuite:local}
container_name: integration-testsuite
environment:
- LOGIN_BASE_URL=http://login:3000
- CYPRESS_CORE_MOCK_STUBS_URL=http://core-mock:22220/v1/stubs
depends_on:
login:
condition: service_started
core-mock:
condition: service_started

View File

@@ -1,4 +1,4 @@
import { stub } from "../support/mock"; import { stub } from "../support/e2e";
describe("verify invite", () => { describe("verify invite", () => {
beforeEach(() => { beforeEach(() => {

View File

@@ -1,4 +1,4 @@
import { stub } from "../support/mock"; import { stub } from "../support/e2e";
describe("login", () => { describe("login", () => {
beforeEach(() => { beforeEach(() => {

View File

@@ -1,4 +1,4 @@
import { stub } from "../support/mock"; import { stub } from "../support/e2e";
const IDP_URL = "https://example.com/idp/url"; const IDP_URL = "https://example.com/idp/url";

View File

@@ -1,4 +1,4 @@
import { stub } from "../support/mock"; import { stub } from "../support/e2e";
describe("register", () => { describe("register", () => {
beforeEach(() => { beforeEach(() => {

View File

@@ -1,4 +1,4 @@
import { stub } from "../support/mock"; import { stub } from "../support/e2e";
describe("verify email", () => { describe("verify email", () => {
beforeEach(() => { beforeEach(() => {

View File

@@ -1,37 +0,0 @@
/// <reference types="cypress" />
// ***********************************************
// This example commands.ts shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
//
// declare global {
// namespace Cypress {
// interface Chainable {
// login(email: string, password: string): Chainable<void>
// drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
// }
// }
// }

View File

@@ -1,20 +1,29 @@
// *********************************************************** const url = Cypress.env('CORE_MOCK_STUBS_URL') || "http://localhost:22220/v1/stubs"
// This example support/e2e.ts is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax: function removeStub(service: string, method: string) {
import "./commands"; return cy.request({
url,
method: "DELETE",
qs: {
service,
method,
},
});
}
// Alternatively you can use CommonJS syntax: export function stub(service: string, method: string, out?: any) {
// require('./commands') removeStub(service, method);
return cy.request({
url,
method: "POST",
body: {
stubs: [
{
service,
method,
out,
},
],
},
});
}

View File

@@ -1,27 +0,0 @@
function removeStub(service: string, method: string) {
return cy.request({
url: "http://localhost:22220/v1/stubs",
method: "DELETE",
qs: {
service,
method,
},
});
}
export function stub(service: string, method: string, out?: any) {
removeStub(service, method);
return cy.request({
url: "http://localhost:22220/v1/stubs",
method: "POST",
body: {
stubs: [
{
service,
method,
out,
},
],
},
});
}

View File

@@ -1,5 +1,4 @@
{ {
"extends": "../tsconfig.json",
"compilerOptions": { "compilerOptions": {
"target": "es5", "target": "es5",
"lib": ["es5", "dom"], "lib": ["es5", "dom"],

View File

@@ -46,6 +46,9 @@ target "core-mock" {
target "login-integration-testsuite" { target "login-integration-testsuite" {
dockerfile = "dockerfiles/login-integration-testsuite.Dockerfile" dockerfile = "dockerfiles/login-integration-testsuite.Dockerfile"
contexts = {
login-base = "target:login-base"
}
} }
# We run integration and acceptance tests against the next standalone server for docker. # We run integration and acceptance tests against the next standalone server for docker.

View File

@@ -3,4 +3,4 @@ ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH" ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable RUN corepack enable
RUN apk add --no-cache libc6-compat bash git RUN apk add --no-cache libc6-compat bash git
WORKDIR /app WORKDIR /build

View File

@@ -1,15 +1,16 @@
FROM cypress/factory AS login-integration-testsuite FROM login-base AS integration-dependencies
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
WORKDIR /opt/app
COPY \ COPY \
pnpm-lock.yaml \ pnpm-lock.yaml \
pnpm-workspace.yaml \ pnpm-workspace.yaml \
./ ./
COPY ./apps/login-integration-testsuite/package.json ./apps/login-integration-testsuite/package.json COPY ./apps/login-integration-testsuite/package.json ./apps/login-integration-testsuite/package.json
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \ RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --frozen-lockfile pnpm install --no-frozen-lockfile --filter=login-integration-testsuite
RUN pnpm exec cypress install
COPY ./apps/login-integration-testsuite/ . FROM cypress/factory AS login-integration-testsuite
CMD ["pnpm", "exec", "cypress", "run"] WORKDIR /opt/app
COPY --from=integration-dependencies /build/apps/login-integration-testsuite .
RUN npm install cypress
RUN npx cypress install
COPY ./apps/login-integration-testsuite .
CMD ["npx", "cypress", "run"]

View File

@@ -3,20 +3,20 @@ RUN pnpm install turbo --global
COPY . . COPY . .
RUN turbo prune @zitadel/login --docker RUN turbo prune @zitadel/login --docker
FROM login-base AS installer FROM login-base AS installer
COPY --from=prune-for-docker /app/out/json/ . COPY --from=prune-for-docker /build/out/json/ .
RUN pnpm install --frozen-lockfile RUN pnpm install --frozen-lockfile
COPY --from=prune-for-docker /app/out/full/ . COPY --from=prune-for-docker /build/out/full/ .
RUN NEXT_PUBLIC_BASE_PATH=/ui/v2/login NEXT_OUTPUT_MODE=standalone pnpm exec turbo run build RUN NEXT_PUBLIC_BASE_PATH=/ui/v2/login NEXT_OUTPUT_MODE=standalone pnpm exec turbo run build
FROM login-platform AS login-standalone FROM login-platform AS login-standalone
WORKDIR /app WORKDIR /runtime
RUN addgroup --system --gid 1001 nodejs && \ RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs adduser --system --uid 1001 nextjs
# If /.env-file/.env is mounted into the container, its variables are made available to the server before it starts up. # If /.env-file/.env is mounted into the container, its variables are made available to the server before it starts up.
RUN mkdir -p /.env-file && touch /.env-file/.env && chown -R nextjs:nodejs /.env-file RUN mkdir -p /.env-file && touch /.env-file/.env && chown -R nextjs:nodejs /.env-file
COPY --chown=nextjs:nodejs --from=installer /app/apps/login/.next/standalone ./ COPY --chown=nextjs:nodejs --from=installer /build/apps/login/.next/standalone ./
COPY --chown=nextjs:nodejs --from=installer /app/apps/login/.next/static ./apps/login/.next/static COPY --chown=nextjs:nodejs --from=installer /build/apps/login/.next/static ./apps/login/.next/static
COPY --chown=nextjs:nodejs --from=installer /app/apps/login/public ./apps/login/public COPY --chown=nextjs:nodejs --from=installer /build/apps/login/public ./apps/login/public
USER nextjs USER nextjs
ENV HOSTNAME="0.0.0.0" ENV HOSTNAME="0.0.0.0"
CMD ["/bin/sh", "-c", " set -o allexport && . /.env-file/.env && set +o allexport && node apps/login/server.js"] CMD ["/bin/sh", "-c", " set -o allexport && . /.env-file/.env && set +o allexport && node apps/login/server.js"]

View File

@@ -3,4 +3,4 @@ COPY packages/zitadel-proto packages/zitadel-proto
RUN pnpm generate RUN pnpm generate
FROM scratch FROM scratch
COPY --from=zitadel-proto /app/packages/zitadel-proto / COPY --from=zitadel-proto /build/packages/zitadel-proto /

View File

@@ -12,6 +12,7 @@ fi
MAKE_TARGET=$1 MAKE_TARGET=$1
IMAGES=$2 IMAGES=$2
FORCE=${FORCE:-false}
DIGEST_FILE="$CACHE_DIR/$MAKE_TARGET.digests" DIGEST_FILE="$CACHE_DIR/$MAKE_TARGET.digests"
mkdir -p "$CACHE_DIR" mkdir -p "$CACHE_DIR"
@@ -31,14 +32,18 @@ OLD_DIGEST=$(cat "$DIGEST_FILE" 2>/dev/null || echo "")
OLD_STATUS=$(echo "$OLD_DIGEST" | cut -d ';' -f1) OLD_STATUS=$(echo "$OLD_DIGEST" | cut -d ';' -f1)
OLD_IDS=$(echo "$OLD_DIGEST" | cut -d ';' -f2-9) OLD_IDS=$(echo "$OLD_DIGEST" | cut -d ';' -f2-9)
if [[ "$OLD_IDS" == "$(get_image_ids)" ]]; then if [[ "$OLD_IDS" == "$(get_image_ids)" ]]; then
if [[ "$FORCE" == "true" ]]; then
echo "\$FORCE=$FORCE - Running $MAKE_TARGET despite unchanged images."
else
echo "Skipping $MAKE_TARGET all images unchanged, returning cached status $OLD_STATUS" echo "Skipping $MAKE_TARGET all images unchanged, returning cached status $OLD_STATUS"
exit $OLD_STATUS exit $OLD_STATUS
else fi
echo "Running $MAKE_TARGET..."
set +e
make $MAKE_TARGET
STATUS=$?
set -e
echo "${STATUS};$(get_image_ids)" > $DIGEST_FILE
exit $STATUS
fi fi
echo "Running $MAKE_TARGET..."
set +e
make -j $MAKE_TARGET
STATUS=$?
set -e
echo "${STATUS};$(get_image_ids)" > $DIGEST_FILE
exit $STATUS