From b39b58f68ef969cb46af601aab55295d6d1c5aa1 Mon Sep 17 00:00:00 2001 From: Vitalii Savitskii Date: Mon, 13 Apr 2026 10:53:35 +0200 Subject: [PATCH 1/5] Upgrade .net version and packages --- .../SnD.ApiClient.Azure.csproj | 7 ++++--- .../SnD.ApiClient.Nexus.Generated.csproj | 2 +- src/SnD.ApiClient/SnD.ApiClient.csproj | 17 ++++++++++------- .../SnD.ApiClient.Tests.Acceptance.csproj | 16 ++++++++-------- .../SnD.ApiClient.Tests.csproj | 8 ++++---- 5 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/SnD.ApiClient.Azure/SnD.ApiClient.Azure.csproj b/src/SnD.ApiClient.Azure/SnD.ApiClient.Azure.csproj index 61280d8..e1fc27e 100644 --- a/src/SnD.ApiClient.Azure/SnD.ApiClient.Azure.csproj +++ b/src/SnD.ApiClient.Azure/SnD.ApiClient.Azure.csproj @@ -1,7 +1,7 @@ - net6.0 + net10.0 latest enable latest @@ -18,8 +18,9 @@ - - + + + diff --git a/src/SnD.ApiClient.Nexus.Generated/SnD.ApiClient.Nexus.Generated.csproj b/src/SnD.ApiClient.Nexus.Generated/SnD.ApiClient.Nexus.Generated.csproj index ac63c4a..0361301 100644 --- a/src/SnD.ApiClient.Nexus.Generated/SnD.ApiClient.Nexus.Generated.csproj +++ b/src/SnD.ApiClient.Nexus.Generated/SnD.ApiClient.Nexus.Generated.csproj @@ -1,7 +1,7 @@  - net6.0 + net10.0 enable enable diff --git a/src/SnD.ApiClient/SnD.ApiClient.csproj b/src/SnD.ApiClient/SnD.ApiClient.csproj index 003bb5d..b545354 100644 --- a/src/SnD.ApiClient/SnD.ApiClient.csproj +++ b/src/SnD.ApiClient/SnD.ApiClient.csproj @@ -1,7 +1,7 @@ - net6.0 + net10.0 enable latest SnD.ApiClient @@ -22,14 +22,17 @@ - - - - + + + + - - + + + + + diff --git a/test/SnD.ApiClient.Tests.Acceptance/SnD.ApiClient.Tests.Acceptance.csproj b/test/SnD.ApiClient.Tests.Acceptance/SnD.ApiClient.Tests.Acceptance.csproj index 649033c..b1064f4 100644 --- a/test/SnD.ApiClient.Tests.Acceptance/SnD.ApiClient.Tests.Acceptance.csproj +++ b/test/SnD.ApiClient.Tests.Acceptance/SnD.ApiClient.Tests.Acceptance.csproj @@ -1,7 +1,7 @@ - net6.0 + net10.0 false @@ -11,13 +11,13 @@ - - - - - - - + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/SnD.ApiClient.Tests/SnD.ApiClient.Tests.csproj b/test/SnD.ApiClient.Tests/SnD.ApiClient.Tests.csproj index 65445e3..df1350f 100644 --- a/test/SnD.ApiClient.Tests/SnD.ApiClient.Tests.csproj +++ b/test/SnD.ApiClient.Tests/SnD.ApiClient.Tests.csproj @@ -1,7 +1,7 @@ - net6.0 + net10.0 false @@ -15,9 +15,9 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + From 847f9810078c593daece2ea0764c5a4020d6f019 Mon Sep 17 00:00:00 2001 From: Vitalii Savitskii Date: Mon, 13 Apr 2026 11:51:29 +0200 Subject: [PATCH 2/5] Add acceptance tests to the build --- .github/workflows/build.yaml | 11 ++ integration_tests/kind.yaml | 14 +++ .../manifests/hello-world-algorithm.yaml | 68 ++++++++++ .../manifests/hello-world-workgroup.yaml | 13 ++ integration_tests/manifests/ingress.yaml | 39 ++++++ integration_tests/manifests/minio.yaml | 116 ++++++++++++++++++ .../manifests/nexus-algorithm-sa.yaml | 12 ++ integration_tests/manifests/scylladb.yaml | 85 +++++++++++++ integration_tests/storage/checkpoints.cql | 29 +++++ integration_tests/storage/prepare-scylla.sh | 33 +++++ .../storage/submission_buffer.cql | 8 ++ justfile | 98 +++++++++++++++ .../Nexus/BoxerAuthenticationProvider.cs | 20 +++ .../Nexus/NexusAcceptancetests.cs | 7 +- .../appsettings.test.json | 6 +- 15 files changed, 554 insertions(+), 5 deletions(-) create mode 100644 integration_tests/kind.yaml create mode 100644 integration_tests/manifests/hello-world-algorithm.yaml create mode 100644 integration_tests/manifests/hello-world-workgroup.yaml create mode 100644 integration_tests/manifests/ingress.yaml create mode 100644 integration_tests/manifests/minio.yaml create mode 100644 integration_tests/manifests/nexus-algorithm-sa.yaml create mode 100644 integration_tests/manifests/scylladb.yaml create mode 100644 integration_tests/storage/checkpoints.cql create mode 100755 integration_tests/storage/prepare-scylla.sh create mode 100644 integration_tests/storage/submission_buffer.cql create mode 100644 justfile create mode 100644 test/SnD.ApiClient.Tests.Acceptance/Nexus/BoxerAuthenticationProvider.cs diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index da88e2d..f9c25c2 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -33,3 +33,14 @@ jobs: with: github-token: ${{ secrets.GITHUB_TOKEN }} lcov-file: ./test/Coverage/coverage.info + + - name: Setup just + uses: extractions/setup-just@v3 + + - name: Setup integration tests + run: just up + + - name: Test + working-directory: ./test/SnD.ApiClient.Tests.Acceptance + run: | + dotnet test SnD.ApiClient.Tests.Acceptance.csproj --configuration Debug --runtime linux-x64 --logger GitHubActions diff --git a/integration_tests/kind.yaml b/integration_tests/kind.yaml new file mode 100644 index 0000000..44c43cb --- /dev/null +++ b/integration_tests/kind.yaml @@ -0,0 +1,14 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +nodes: + - role: control-plane + extraPortMappings: + - containerPort: 80 + hostPort: 5555 + protocol: TCP + - containerPort: 30043 + hostPort: 9000 + protocol: TCP + - containerPort: 30042 + hostPort: 9042 + protocol: TCP diff --git a/integration_tests/manifests/hello-world-algorithm.yaml b/integration_tests/manifests/hello-world-algorithm.yaml new file mode 100644 index 0000000..4222b12 --- /dev/null +++ b/integration_tests/manifests/hello-world-algorithm.yaml @@ -0,0 +1,68 @@ +apiVersion: science.sneaksanddata.com/v1 +kind: NexusAlgorithmTemplate +metadata: + name: hello-world + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-weight": "0" + "helm.sh/hook-delete-policy": before-hook-creation +spec: + args: + - main.py + - '--sas-uri=%s' + - '--request-id=%s' + command: python + computeResources: + cpuLimit: 1000m + memoryLimit: 1000Mi + container: + image: nexus-hello + registry: ghcr.io/sneaksanddata + serviceAccountName: nexus + versionTag: 0.1.2 + datadogIntegrationSettings: + mountDatadogSocket: true + errorHandlingBehaviour: + fatalExitCodes: + - 137 + transientExitCodes: + - 139 + runtimeEnvironment: + deadlineSeconds: 120 + environmentVariables: + - name: DD_AGENT_HOST + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: PROTEUS__DD_SITE + value: datadoghq.eu + - name: NEXUS__ALGORITHM_NAME + value: nexus-hello + - name: NEXUS__SCHEDULER_URL + value: http://nexus.nexus.svc.cluster.local:8080 + - name: NEXUS__RECEIVER_URL + value: http://dockerhost.nexus.svc.cluster.local:8081 + - name: NEXUS__STORAGE_CLIENT_CLASS + value: adapta.storage.blob.s3_storage_client.S3StorageClient + - name: ALGORITHM_STORAGE__BASE_PATH + value: s3a://nexus/storage + - name: NEXUS__ALGORITHM_OUTPUT_PATH + value: s3a://nexus/result + - name: NEXUS__TELEMETRY_PATH + value: s3a://nexus/telemetry + - name: ALGORITHM_STORAGE_TYPE + value: S3 + - name: PROTEUS__AWS_REGION + value: us-east-1 + - name: PROTEUS__AWS_ENDPOINT + value: http://dockerhost.nexus.svc.cluster.local:9000 + - name: PROTEUS__AWS_SECRET_ACCESS_KEY + value: minioadmin + - name: PROTEUS__AWS_ACCESS_KEY_ID + value: minioadmin + mappedEnvironmentVariables: [] + maximumRetries: 1 + workgroupRef: + group: science.sneaksanddata.com + kind: NexusAlgorithmWorkgroup + name: default diff --git a/integration_tests/manifests/hello-world-workgroup.yaml b/integration_tests/manifests/hello-world-workgroup.yaml new file mode 100644 index 0000000..5ed56b3 --- /dev/null +++ b/integration_tests/manifests/hello-world-workgroup.yaml @@ -0,0 +1,13 @@ +apiVersion: science.sneaksanddata.com/v1 +kind: NexusAlgorithmWorkgroup +metadata: + name: default + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": before-hook-creation +spec: + affinity: {} + cluster: kind-nexus-shard-0 + description: Default workgroup + tolerations: [] \ No newline at end of file diff --git a/integration_tests/manifests/ingress.yaml b/integration_tests/manifests/ingress.yaml new file mode 100644 index 0000000..bc69ec2 --- /dev/null +++ b/integration_tests/manifests/ingress.yaml @@ -0,0 +1,39 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: receiver + annotations: + nginx.ingress.kubernetes.io/use-regex: "true" + nginx.ingress.kubernetes.io/rewrite-target: /$2 +spec: + ingressClassName: nginx + rules: + - http: + paths: + - pathType: ImplementationSpecific + path: /receiver(/|$)(.*) + backend: + service: + name: nexus-receiver + port: + number: 8080 +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: scheduler + annotations: + nginx.ingress.kubernetes.io/use-regex: "true" + nginx.ingress.kubernetes.io/rewrite-target: /$2 +spec: + ingressClassName: nginx + rules: + - http: + paths: + - pathType: ImplementationSpecific + path: /scheduler(/|$)(.*) + backend: + service: + name: nexus + port: + number: 8080 diff --git a/integration_tests/manifests/minio.yaml b/integration_tests/manifests/minio.yaml new file mode 100644 index 0000000..5e23bae --- /dev/null +++ b/integration_tests/manifests/minio.yaml @@ -0,0 +1,116 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: minio + labels: + app: minio +spec: + replicas: 1 + selector: + matchLabels: + app: minio + template: + metadata: + labels: + app: minio + spec: + hostname: minio-e2e + containers: + - name: minio + image: quay.io/minio/minio + args: + - server + - /data + - "--console-address" + - ":9123" + ports: + - containerPort: 9000 + - containerPort: 9123 + livenessProbe: + httpGet: + path: / + port: 9123 + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 5 + readinessProbe: + httpGet: + path: / + port: 9123 + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 5 +--- +apiVersion: v1 +kind: Service +metadata: + name: minio +spec: + selector: + app: minio + ports: + - name: api + port: 9000 + targetPort: 9000 + - name: console + port: 9123 + targetPort: 9123 + type: ClusterIP +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: minio-setup +spec: + backoffLimit: 5 + template: + spec: + restartPolicy: OnFailure + containers: + - name: minio-setup + image: quay.io/minio/minio + command: + - /bin/sh + - -c + - | + echo "Waiting for Minio to be available via mc..." + max_retries=30 + attempt=1 + until mc alias set e2e "http://minio.default.svc.cluster.local:9000" minioadmin minioadmin >/dev/null 2>&1; do + if (( attempt >= max_retries )); then + echo "minio did not become available after $max_retries attempts." + exit 1 + fi + sleep 1 + ((attempt++)) + done + echo "mc is available." + mc admin info e2e + mc mb --ignore-existing e2e/tmp + mc mb --ignore-existing e2e/nexus + mc mb --ignore-existing e2e/nexus-sdk-tests +--- +apiVersion: v1 +kind: Service +metadata: + name: minio-node-port +spec: + selector: + app: minio + ports: + - port: 9000 + targetPort: 9000 + nodePort: 30043 + type: NodePort +--- +apiVersion: v1 +kind: Secret +metadata: + name: minio-credentials +stringData: + NEXUS__S3_BUFFER__ACCESS_KEY_ID: "minioadmin" + NEXUS__S3_BUFFER__SECRET_ACCESS_KEY: "minioadmin" + NEXUS__S3_BUFFER__ENDPOINT: "http://minio.default.svc.cluster.local:9000" + NEXUS__S3_BUFFER__REGION: "us-east-1" diff --git a/integration_tests/manifests/nexus-algorithm-sa.yaml b/integration_tests/manifests/nexus-algorithm-sa.yaml new file mode 100644 index 0000000..9fe2bc2 --- /dev/null +++ b/integration_tests/manifests/nexus-algorithm-sa.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: nexus:serviceaccount:nexus +subjects: + - kind: ServiceAccount + name: nexus + namespace: default +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: admin diff --git a/integration_tests/manifests/scylladb.yaml b/integration_tests/manifests/scylladb.yaml new file mode 100644 index 0000000..58e7b9a --- /dev/null +++ b/integration_tests/manifests/scylladb.yaml @@ -0,0 +1,85 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: scylla +spec: + replicas: 1 + selector: + matchLabels: + app: scylla + template: + metadata: + labels: + app: scylla + spec: + initContainers: + - name: create-scylla-yaml + image: busybox + command: ["/bin/sh", "-c", "touch /scylla-conf/scylla.yaml"] + volumeMounts: + - name: scylla-conf + mountPath: /scylla-conf + containers: + - name: scylla + image: scylladb/scylla + command: ["scylla"] + args: + - "--rpc-address" + - "0.0.0.0" + - "--broadcast-rpc-address" + - "127.0.0.1" + - "--smp" + - "1" + - "--developer-mode=1" + livenessProbe: + exec: + command: + - /bin/sh + - -c + - '[ "$(nodetool statusgossip)" = "running" ]' + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 5 + volumeMounts: + - name: scylla-conf + mountPath: /conf/scylla.yaml + subPath: scylla.yaml + volumes: + - name: scylla-conf + emptyDir: {} +--- +apiVersion: v1 +kind: Service +metadata: + name: scylla +spec: + selector: + app: scylla + ports: + - name: cql + port: 9042 + targetPort: 9042 + type: ClusterIP +--- +apiVersion: v1 +kind: Secret +metadata: + name: cassandra-credentials +stringData: + NEXUS__SCYLLA_CQL_STORE__HOSTS: "scylla.default.svc.cluster.local" + NEXUS__SCYLLA_CQL_STORE__USER: "cassandra" + NEXUS__SCYLLA_CQL_STORE__PASSWORD: "cassandra" +--- +apiVersion: v1 +kind: Service +metadata: + name: scylla-node-port +spec: + selector: + app: scylla + ports: + - port: 9042 + targetPort: 9042 + nodePort: 30042 + type: NodePort diff --git a/integration_tests/storage/checkpoints.cql b/integration_tests/storage/checkpoints.cql new file mode 100644 index 0000000..c807069 --- /dev/null +++ b/integration_tests/storage/checkpoints.cql @@ -0,0 +1,29 @@ +create table nexus.checkpoints +( + algorithm text, + id text, + lifecycle_stage text, + payload_uri text, + result_uri text, + algorithm_failure_cause text, + algorithm_failure_details text, + received_by_host text, + received_at timestamp, + sent_at timestamp, + applied_configuration text, + configuration_overrides text, + content_hash text, + last_modified timestamp, + tag text, + api_version text, + job_uid text, + parent text, + payload_valid_for text, + PRIMARY KEY ((algorithm, id)) +); + +create index submission_tag ON nexus.checkpoints (tag); + +create index host ON nexus.checkpoints (received_by_host); + +create index lifecycle_stage ON nexus.checkpoints (lifecycle_stage); diff --git a/integration_tests/storage/prepare-scylla.sh b/integration_tests/storage/prepare-scylla.sh new file mode 100755 index 0000000..217897a --- /dev/null +++ b/integration_tests/storage/prepare-scylla.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +# Wait for cqlsh to be ready by running 'describe keyspaces', retry max 30 times +echo "Waiting for ScyllaDB to be available via cqlsh..." +max_retries=30 +attempt=1 +until cqlsh localhost -e "DESCRIBE KEYSPACES;" >/dev/null 2>&1; do + if (( attempt >= max_retries )); then + echo "cqlsh did not become available after $max_retries attempts." + exit 1 + fi + sleep 1 + ((attempt++)) +done +echo "cqlsh is available." + +cqlsh localhost -e "CREATE KEYSPACE nexus WITH replication = { 'class': 'SimpleStrategy', 'replication_factor': 1 };" + +echo 'Applying checkpoints table' + +cqlsh localhost -f /opt/storage/checkpoints.cql + +echo 'Checking table' + +cqlsh localhost -e 'SELECT * FROM nexus.checkpoints' + +echo 'Applying submission_buffer table' + +cqlsh localhost -f /opt/storage/submission_buffer.cql + +echo 'Checking table' + +cqlsh localhost -e 'SELECT * FROM nexus.submission_buffer' diff --git a/integration_tests/storage/submission_buffer.cql b/integration_tests/storage/submission_buffer.cql new file mode 100644 index 0000000..b1868a5 --- /dev/null +++ b/integration_tests/storage/submission_buffer.cql @@ -0,0 +1,8 @@ +create table nexus.submission_buffer +( + algorithm text, + id text, + cluster text, + template text, + PRIMARY KEY ((algorithm, id)) +); diff --git a/justfile b/justfile new file mode 100644 index 0000000..47376b6 --- /dev/null +++ b/justfile @@ -0,0 +1,98 @@ +default: + @just --list + +fresh: stop up + +up: start-kind-cluster \ + install-ingress-controller \ + create-ingress \ + scylla \ + dbschema \ + shards-kubeconfig \ + crd \ + algorithm \ + scheduler \ + receiver \ + supervisor \ + minio \ + wait-for-services + +stop: + kind delete cluster + +start-kind-cluster: + kind create cluster --config=integration_tests/kind.yaml + +install-ingress-controller: + kubectl apply -f https://kind.sigs.k8s.io/examples/ingress/deploy-ingress-nginx.yaml + kubectl rollout status deployment/ingress-nginx-controller -n ingress-nginx --timeout=180s + +create-ingress: + # Create ingress rules for services + for i in $(seq 1 30); do \ + kubectl apply -f ./integration_tests/manifests/ingress.yaml && break || \ + (echo "Retry $i/30: failed to apply ingress, retrying in 1s..." && sleep 1); \ + done; \ + if [ $i -eq 30 ]; then \ + echo "Failed to apply ingress after 30 attempts."; \ + exit 1; \ + fi + +scylla: + kubectl apply -f integration_tests/manifests/scylladb.yaml + kubectl rollout status deployment/scylla --timeout=180s + +minio: + kubectl apply -f integration_tests/manifests/minio.yaml + kubectl rollout status deployment/minio --timeout=180s + +crd: + helm upgrade --install nexus-crd oci://ghcr.io/sneaksanddata/helm/nexus-crd --version v1.0.0 + +algorithm: + kubectl apply -f integration_tests/manifests/hello-world-algorithm.yaml + kubectl apply -f integration_tests/manifests/hello-world-workgroup.yaml + kubectl apply -f integration_tests/manifests/nexus-algorithm-sa.yaml + +shards-kubeconfig: + kind get kubeconfig \ + | yq -o=json '.clusters[].cluster.server = "https://kubernetes.default.svc.cluster.local"' \ + | kubectl create secret generic nexus-shards --from-file=kind-nexus-shard-0.kubeconfig=/dev/stdin --type=Opaque --dry-run=client -o yaml \ + | kubectl apply -f - + +scheduler: + helm upgrade --install nexus oci://ghcr.io/sneaksanddata/helm/nexus --version v1.1.12 \ + --set scheduler.replicas=1 \ + --set scheduler.config.cqlStore.type=scylla \ + --set scheduler.config.cqlStore.secretRefEnabled=true \ + --set 'extraEnvFrom[0].secretRef.name=cassandra-credentials' \ + --set 'scheduler.config.cqlStore.secretName=cassandra-credentials' \ + --set 'scheduler.config.s3Buffer.s3Credentials.secretRefEnabled=true' \ + --set 'scheduler.config.s3Buffer.s3Credentials.secretName=minio-credentials' \ + --set 'scheduler.config.s3Buffer.processing.payloadStoragePath=s3a://nexus' + + +receiver: + helm upgrade --install nexus-receiver oci://ghcr.io/sneaksanddata/helm/nexus-receiver --version v1.1.4 \ + --set receiver.replicas=1 \ + --set receiver.config.cqlStore.type=scylla \ + --set receiver.config.cqlStore.secretRefEnabled=true \ + --set 'extraEnvFrom[0].secretRef.name=cassandra-credentials' \ + --set receiver.config.cqlStore.secretName="cassandra-credentials" + +supervisor: + helm upgrade --install nexus-supervisor oci://ghcr.io/sneaksanddata/helm/nexus-supervisor --version v0.1.6 \ + --set 'extraEnvFrom[0].secretRef.name=cassandra-credentials' \ + --set 'supervisor.replicas=1' \ + --set 'supervisor.config.cqlStore.type=scylla' \ + --set 'supervisor.config.cqlStore.secretRefEnabled=true' \ + --set 'supervisor.config.cqlStore.secretName=cassandra-credentials' \ + --set 'supervisor.config.resourceNamespace=default' + +wait-for-services: + kubectl rollout status deployment/nexus --timeout=180s + kubectl rollout status deployment/nexus-receiver --timeout=180s + kubectl rollout status deployment/nexus-supervisor --timeout=180s + +dbschema: + docker run --rm -v $(pwd)/integration_tests/storage:/opt/storage --network=host --entrypoint /opt/storage/prepare-scylla.sh scylladb/scylla:5.0.1 \ No newline at end of file diff --git a/test/SnD.ApiClient.Tests.Acceptance/Nexus/BoxerAuthenticationProvider.cs b/test/SnD.ApiClient.Tests.Acceptance/Nexus/BoxerAuthenticationProvider.cs new file mode 100644 index 0000000..ce21a2f --- /dev/null +++ b/test/SnD.ApiClient.Tests.Acceptance/Nexus/BoxerAuthenticationProvider.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Kiota.Abstractions; +using Microsoft.Kiota.Abstractions.Authentication; + +namespace SnD.ApiClient.Tests.Acceptance.Nexus; + +public class EmptyAuthenticationProvider: IAuthenticationProvider +{ + private const string Token = "mockToken"; + + public Task AuthenticateRequestAsync(RequestInformation request, + Dictionary? additionalAuthenticationContext = null, + CancellationToken cancellationToken = default) + { + request.Headers.Add("Authorization", $"Bearer {Token}"); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/test/SnD.ApiClient.Tests.Acceptance/Nexus/NexusAcceptancetests.cs b/test/SnD.ApiClient.Tests.Acceptance/Nexus/NexusAcceptancetests.cs index 419a7ff..da1d474 100644 --- a/test/SnD.ApiClient.Tests.Acceptance/Nexus/NexusAcceptancetests.cs +++ b/test/SnD.ApiClient.Tests.Acceptance/Nexus/NexusAcceptancetests.cs @@ -7,7 +7,9 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Microsoft.Kiota.Abstractions.Authentication; using SnD.ApiClient.Azure; +using SnD.ApiClient.Boxer; using SnD.ApiClient.Config; using SnD.ApiClient.Extensions; using SnD.ApiClient.Nexus; @@ -33,8 +35,9 @@ public NexusAcceptanceTests() .Configure(configurationRoot.GetSection(nameof(BoxerTokenProviderOptions))) .AddSingleton() .AddLogging(conf => conf.AddConsole()) - .AuthorizeWithBoxerOnAzure() - .AddAuthenticationProvider() + // .AuthorizeWithBoxerOnAzure() + // .AddAuthenticationProvider() + .AddSingleton() .Configure(configurationRoot.GetSection(nameof(NexusClientOptions))) .AddNexusRetryPolicy(sp => new RetryAllErrors(sp.GetRequiredService>(), sp.GetRequiredService>())) diff --git a/test/SnD.ApiClient.Tests.Acceptance/appsettings.test.json b/test/SnD.ApiClient.Tests.Acceptance/appsettings.test.json index af7acb5..330f48a 100644 --- a/test/SnD.ApiClient.Tests.Acceptance/appsettings.test.json +++ b/test/SnD.ApiClient.Tests.Acceptance/appsettings.test.json @@ -11,15 +11,15 @@ "IdentityProvider": "" }, "NexusClientOptions": { - "BaseUri": "", + "BaseUri": "http://localhost:5555/scheduler", "MaxRetryAttempts": 3, "RetryIntervalSeconds": 5 }, "AcceptanceTestsConfiguration": { "AlgorithmRequest": {}, - "AlgorithmName": "", + "AlgorithmName": "hello-world", "AlgorithmConfiguration": {}, - "AlgorithmPayload": "" + "AlgorithmPayload": "{\"hello_text\": \"hello\", \"hello_author\": \"world\"}", } } \ No newline at end of file From 729a7873be5bf2418ee1cd2f943dff1ada2c5831 Mon Sep 17 00:00:00 2001 From: Vitalii Savitskii Date: Mon, 13 Apr 2026 12:05:42 +0200 Subject: [PATCH 3/5] Try to fix --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f9c25c2..468b6aa 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -43,4 +43,4 @@ jobs: - name: Test working-directory: ./test/SnD.ApiClient.Tests.Acceptance run: | - dotnet test SnD.ApiClient.Tests.Acceptance.csproj --configuration Debug --runtime linux-x64 --logger GitHubActions + dotnet test SnD.ApiClient.Tests.Acceptance.csproj --configuration Debug --runtime linux-x64 From 13b84b1b5b59ac0f52a31f02a15f2bdf0d566ec2 Mon Sep 17 00:00:00 2001 From: Vitalii Savitskii Date: Mon, 13 Apr 2026 14:54:31 +0200 Subject: [PATCH 4/5] Fix --- .../Config/AcceptanceTestsConfiguration.cs | 6 ++++++ ...Provider.cs => EmptyAuthenticationProvider.cs} | 0 .../Nexus/NexusAcceptancetests.cs | 15 ++++++++------- .../appsettings.test.json | 3 ++- 4 files changed, 16 insertions(+), 8 deletions(-) rename test/SnD.ApiClient.Tests.Acceptance/Nexus/{BoxerAuthenticationProvider.cs => EmptyAuthenticationProvider.cs} (100%) diff --git a/test/SnD.ApiClient.Tests.Acceptance/Config/AcceptanceTestsConfiguration.cs b/test/SnD.ApiClient.Tests.Acceptance/Config/AcceptanceTestsConfiguration.cs index af22089..e630f0d 100644 --- a/test/SnD.ApiClient.Tests.Acceptance/Config/AcceptanceTestsConfiguration.cs +++ b/test/SnD.ApiClient.Tests.Acceptance/Config/AcceptanceTestsConfiguration.cs @@ -18,4 +18,10 @@ public class AcceptanceTestsConfiguration /// Payload serialized to string /// public string AlgorithmPayload { get; set; } + + /// + /// Use BoxerTokenProvider as authentication provider for NexusClient on Azure environment. + /// If false will add EmptyTokenProvider which does not interact with Boxer. + /// + public bool UseBoxerTokenProviderOnAzure { get; set; } } \ No newline at end of file diff --git a/test/SnD.ApiClient.Tests.Acceptance/Nexus/BoxerAuthenticationProvider.cs b/test/SnD.ApiClient.Tests.Acceptance/Nexus/EmptyAuthenticationProvider.cs similarity index 100% rename from test/SnD.ApiClient.Tests.Acceptance/Nexus/BoxerAuthenticationProvider.cs rename to test/SnD.ApiClient.Tests.Acceptance/Nexus/EmptyAuthenticationProvider.cs diff --git a/test/SnD.ApiClient.Tests.Acceptance/Nexus/NexusAcceptancetests.cs b/test/SnD.ApiClient.Tests.Acceptance/Nexus/NexusAcceptancetests.cs index da1d474..f1653cc 100644 --- a/test/SnD.ApiClient.Tests.Acceptance/Nexus/NexusAcceptancetests.cs +++ b/test/SnD.ApiClient.Tests.Acceptance/Nexus/NexusAcceptancetests.cs @@ -9,7 +9,6 @@ using Microsoft.Extensions.Options; using Microsoft.Kiota.Abstractions.Authentication; using SnD.ApiClient.Azure; -using SnD.ApiClient.Boxer; using SnD.ApiClient.Config; using SnD.ApiClient.Extensions; using SnD.ApiClient.Nexus; @@ -31,18 +30,20 @@ public NexusAcceptanceTests() var configurationRoot = new ConfigurationBuilder().AddJsonFile("appsettings.test.json").Build(); configurationRoot.GetSection(nameof(AcceptanceTestsConfiguration)).Bind(this.configuration); - this.services = new ServiceCollection() + var serviceCollection = new ServiceCollection() .Configure(configurationRoot.GetSection(nameof(BoxerTokenProviderOptions))) .AddSingleton() .AddLogging(conf => conf.AddConsole()) - // .AuthorizeWithBoxerOnAzure() - // .AddAuthenticationProvider() - .AddSingleton() .Configure(configurationRoot.GetSection(nameof(NexusClientOptions))) .AddNexusRetryPolicy(sp => new RetryAllErrors(sp.GetRequiredService>(), sp.GetRequiredService>())) - .AddNexusClient() - .BuildServiceProvider(); + .AddNexusClient(); + + serviceCollection = configuration.UseBoxerTokenProviderOnAzure + ? serviceCollection.AuthorizeWithBoxerOnAzure().AddAuthenticationProvider() + : serviceCollection.AddSingleton(); + + this.services = serviceCollection.BuildServiceProvider(); } [SkippableFact] diff --git a/test/SnD.ApiClient.Tests.Acceptance/appsettings.test.json b/test/SnD.ApiClient.Tests.Acceptance/appsettings.test.json index 330f48a..2dea489 100644 --- a/test/SnD.ApiClient.Tests.Acceptance/appsettings.test.json +++ b/test/SnD.ApiClient.Tests.Acceptance/appsettings.test.json @@ -10,6 +10,7 @@ "BaseUri": "", "IdentityProvider": "" }, + "UseBoxerTokenProviderOnAzure": false, "NexusClientOptions": { "BaseUri": "http://localhost:5555/scheduler", "MaxRetryAttempts": 3, @@ -20,6 +21,6 @@ "AlgorithmRequest": {}, "AlgorithmName": "hello-world", "AlgorithmConfiguration": {}, - "AlgorithmPayload": "{\"hello_text\": \"hello\", \"hello_author\": \"world\"}", + "AlgorithmPayload": "{\"hello_text\": \"hello\", \"hello_author\": \"world\"}" } } \ No newline at end of file From 48acd81fe55a749c43f4790d4b81045dbb786d10 Mon Sep 17 00:00:00 2001 From: Vitalii Savitskii Date: Mon, 13 Apr 2026 14:58:43 +0200 Subject: [PATCH 5/5] Add logs --- .github/workflows/build.yaml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 468b6aa..638a92e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -22,7 +22,7 @@ jobs: run: dotnet restore - name: Build run: dotnet build --no-restore - - name: Test + - name: Run unit tests working-directory: ./test/SnD.ApiClient.Tests run: | dotnet add package coverlet.msbuild && @@ -40,7 +40,12 @@ jobs: - name: Setup integration tests run: just up - - name: Test + - name: Run acceptance tests working-directory: ./test/SnD.ApiClient.Tests.Acceptance run: | dotnet test SnD.ApiClient.Tests.Acceptance.csproj --configuration Debug --runtime linux-x64 + + - name: Get logs from application pods + if: always() + run: | + kubectl describe pods --namespace default || true