Compare commits

..

6 Commits

Author SHA1 Message Date
Jedd Morgan ee0653a9a8 fix tests
.NET Build and Publish / build (push) Has been cancelled
2025-12-02 16:26:37 +00:00
Jedd Morgan 5c40a74f6d fake a release
.NET Build and Publish / build (push) Has been cancelled
2025-12-02 14:45:58 +00:00
Jedd Morgan 21c5a2e163 Fixes 2025-12-02 14:25:17 +00:00
Jedd Morgan 34aa4f3548 subscriptions 2025-12-02 14:24:50 +00:00
Jedd Morgan 3fbd9c17ba format
.NET Build and Publish / build (push) Has been cancelled
2025-11-24 18:41:10 +00:00
Jedd Morgan 937eb94730 First pass 2025-11-24 18:40:17 +00:00
19 changed files with 22 additions and 274 deletions
+5 -14
View File
@@ -6,12 +6,10 @@ on:
docker-compose-file:
required: true
type: string
use-internal-image:
use-github-container-registry:
default: false
type: boolean
secrets:
CODECOV_TOKEN:
required: true
jobs:
integration-test:
env:
@@ -19,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Setup .NET
uses: actions/setup-dotnet@v5
@@ -29,7 +27,7 @@ jobs:
cache-dependency-path: "**/packages.lock.json"
- name: 🔐 Login to Github Container Registry
if: ${{ inputs.use-internal-image }}
if: ${{ inputs.use-github-container-registry }}
uses: docker/login-action@v3
with:
registry: "ghcr.io"
@@ -45,18 +43,11 @@ jobs:
- name: 🏗️ Build
run: dotnet build ${{ env.Solution }} --configuration Release --no-restore -warnaserror
- name: 🔨 Integration Tests against Public Server
if: ${{ !inputs.use-internal-image }}
run: dotnet test ${{ env.Solution }} --filter "(Category=Integration)&(Server!=Internal)" --configuration Release --no-build --no-restore --verbosity=normal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage
- name: 🔨 Integration Tests against Internal Server
if: ${{ inputs.use-internal-image }}
- name: 🔨 Integration Tests
run: dotnet test ${{ env.Solution }} --filter "Category=Integration" --configuration Release --no-build --no-restore --verbosity=normal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage
- name: Upload coverage reports to Codecov with GitHub Action
uses: codecov/codecov-action@v5
continue-on-error: true
with:
fail_ci_if_error: true
files: tests/**/coverage.xml
token: ${{ secrets.CODECOV_TOKEN }}
+3 -9
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Setup .NET
uses: actions/setup-dotnet@v5
@@ -39,9 +39,7 @@ jobs:
- name: Upload coverage reports to Codecov with GitHub Action
uses: codecov/codecov-action@v5
continue-on-error: true
with:
fail_ci_if_error: true
files: tests/**/coverage.xml
token: ${{ secrets.CODECOV_TOKEN }}
@@ -49,13 +47,9 @@ jobs:
uses: "./.github/workflows/integration-test.yml"
with:
docker-compose-file: "docker-compose-internal.yml"
use-internal-image: true
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
use-github-container-registry: true
integration-test-public:
uses: "./.github/workflows/integration-test.yml"
with:
docker-compose-file: "docker-compose.yml"
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
+1 -3
View File
@@ -14,7 +14,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Setup .NET
uses: actions/setup-dotnet@v5
@@ -48,9 +48,7 @@ jobs:
- name: Upload coverage reports to Codecov with GitHub Action
uses: codecov/codecov-action@v5
continue-on-error: true
with:
fail_ci_if_error: true
files: tests/**/coverage.xml
token: ${{ secrets.CODECOV_TOKEN }}
-1
View File
@@ -1 +0,0 @@
dotnet 8.0.400
-1
View File
@@ -17,7 +17,6 @@
<File Path=".github\git-commit-instructions.md" />
</Folder>
<Folder Name="/config/workflows/">
<File Path=".github/workflows/integration-test.yml" />
<File Path=".github/workflows/pr.yml" />
<File Path=".github/workflows/release.yml" />
</Folder>
+1 -1
View File
@@ -180,6 +180,6 @@ Target(
}
);
Target("default", dependsOn: [FORMAT, TEST, INTEGRATION], () => Console.WriteLine("Done!"));
Target("default", dependsOn: [FORMAT, TEST], () => Console.WriteLine("Done!"));
await RunTargetsAndExitAsync(args).ConfigureAwait(true);
@@ -63,7 +63,7 @@ internal sealed class AutomationContext(IOperations operations) : IAutomationCon
);
}
Base rootObject = await operations
Base? rootObject = await operations
.Receive2(
SpeckleClient.ServerUrl,
AutomationRunData.ProjectId,
@@ -74,10 +74,6 @@ internal sealed class AutomationContext(IOperations operations) : IAutomationCon
)
.ConfigureAwait(false);
await SpeckleClient
.Version.Received(new(version.id, AutomationRunData.ProjectId, "automate_function"), cancellationToken)
.ConfigureAwait(false);
Console.WriteLine($"It took {Elapsed.TotalSeconds} seconds to receive the speckle version {versionId}");
return rootObject;
}
-6
View File
@@ -1,6 +0,0 @@
using Speckle.Sdk.Models;
namespace Speckle.Objects.Geometry;
[SpeckleType("Objects.Geometry.SolidX")]
public class SolidX : RawEncodedObject;
-2
View File
@@ -20,6 +20,4 @@ public class RawEncoding : Base // note: at this stage, since we're using this f
public static class RawEncodingFormats
{
public const string RHINO_3DM = "3dm";
public const string ACAD_DWG = "dwg";
public const string ACAD_SAT = "sat";
}
@@ -36,15 +36,6 @@ public record ModelIngestionFailedInput(
public record ModelIngestionCancelledInput(string ingestionId, string projectId, string cancellationMessage);
public record ModelIngestionStartProcessingInput(
string ingestionId,
string projectId,
string progressMessage,
SourceDataInput sourceData
);
public record ModelIngestionRequeueInput(string ingestionId, string projectId, string progressMessage);
public record ProjectModelIngestionSubscriptionInput(
string projectId,
ModelIngestionReference ingestionReference,
@@ -5,9 +5,6 @@ using Speckle.Sdk.Api.GraphQL.Models.Responses;
namespace Speckle.Sdk.Api.GraphQL.Resources;
/// <remarks>
/// Model Ingestion API is available for server versions <c>3.0.3-alpha.583</c> and above
/// </remarks>
public sealed class ModelIngestionResource
{
private readonly ISpeckleGraphQLClient _client;
@@ -17,14 +14,6 @@ public sealed class ModelIngestionResource
_client = client;
}
/// <summary>
/// Create a new model ingestion
/// </summary>
/// <remarks>
/// The model ingestion created will have a <c>processing</c> state (not <c>queued</c>). This mutation is designed to be used
/// by client/connectors that are immediately processing
/// Model Ingestion API is available for server versions <c>3.0.3-alpha.583</c> and above
/// </remarks>
/// <param name="input"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
@@ -71,113 +60,6 @@ public sealed class ModelIngestionResource
return res.data.data.data;
}
/// <summary>
/// For File Import / Cloud integrations only
/// </summary>
/// <remarks>
/// Model Ingestion API is available for server versions <c>3.0.3-alpha.583</c> and above
/// </remarks>
/// <param name="input"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <inheritdoc cref="ISpeckleGraphQLClient.ExecuteGraphQLRequest{T}"/>
public async Task<ModelIngestion> StartProcessing(
ModelIngestionStartProcessingInput input,
CancellationToken cancellationToken = default
)
{
//language=graphql
const string QUERY = """
mutation IngestionStartProcessing($input: ModelIngestionStartProcessingInput!) {
data: projectMutations {
data: modelIngestionMutations {
data: startProcessing(input: $input) {
id
createdAt
updatedAt
modelId
cancellationRequested
statusData {
... on HasModelIngestionStatus {
status
}
... on HasProgressMessage {
progressMessage
}
}
}
}
}
}
""";
GraphQLRequest request = new() { Query = QUERY, Variables = new { input } };
var res = await _client
.ExecuteGraphQLRequest<RequiredResponse<RequiredResponse<RequiredResponse<ModelIngestion>>>>(
request,
cancellationToken
)
.ConfigureAwait(false);
return res.data.data.data;
}
/// <summary>
/// For File Import / Cloud integrations only
/// </summary>
/// <remarks>
/// Model Ingestion API is available for server versions <c>3.0.3-alpha.583</c> and above
/// </remarks>
/// <param name="input"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <inheritdoc cref="ISpeckleGraphQLClient.ExecuteGraphQLRequest{T}"/>
public async Task<ModelIngestion> Requeue(
ModelIngestionRequeueInput input,
CancellationToken cancellationToken = default
)
{
//language=graphql
const string QUERY = """
mutation IngestionStartProcessing($input: ModelIngestionRequeueInput!) {
data: projectMutations {
data: modelIngestionMutations {
data: requeue(input: $input) {
id
createdAt
updatedAt
modelId
cancellationRequested
statusData {
... on HasModelIngestionStatus {
status
}
... on HasProgressMessage {
progressMessage
}
}
}
}
}
}
""";
GraphQLRequest request = new() { Query = QUERY, Variables = new { input } };
var res = await _client
.ExecuteGraphQLRequest<RequiredResponse<RequiredResponse<RequiredResponse<ModelIngestion>>>>(
request,
cancellationToken
)
.ConfigureAwait(false);
return res.data.data.data;
}
/// <remarks>
/// Model Ingestion API is available for server versions <c>3.0.3-alpha.583</c> and above
/// </remarks>
/// <param name="input"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
@@ -230,9 +112,6 @@ public sealed class ModelIngestionResource
/// Request that the server completes the ingestion by creating a version
/// If successful, the job will be in a terminal "successful" state.
/// </summary>
/// <remarks>
/// Model Ingestion API is available for server versions <c>3.0.3-alpha.583</c> and above
/// </remarks>
/// <seealso cref="FailWithError"/>
/// <seealso cref="FailWithCancel"/>
/// <param name="input"></param>
@@ -273,8 +152,7 @@ public sealed class ModelIngestionResource
/// Fail the job with an error.
/// </summary>
/// <remarks>
/// For requested user cancellation, use <see cref="FailWithCancel"/> instead<br/>
/// Model Ingestion API is available for server versions <c>3.0.3-alpha.583</c> and above
/// For requested user cancellation, use <see cref="FailWithCancel"/> instead
/// </remarks>
/// <seealso cref="FailWithCancel"/>
/// <seealso cref="Complete"/>
@@ -328,8 +206,7 @@ public sealed class ModelIngestionResource
/// Fail the ingestion with a <c>canceled</c> status.
/// This should only be done if the user has explicitly requested cancellation
/// Other forms of cancellation use <see cref="FailWithError"/>.
/// The ingestion should then enter a terminal "canceled" state.<br/>
/// Model Ingestion API is available for server versions <c>3.0.3-alpha.583</c> and above
/// The ingestion should then enter a terminal "canceled" state
/// </summary>
/// <seealso cref="FailWithError"/>
/// <seealso cref="Complete"/>
@@ -387,8 +264,7 @@ public sealed class ModelIngestionResource
/// It's up to the client to observe this cancellation request
/// via <see cref="SubscriptionResource.CreateProjectModelIngestionCancellationRequestedSubscription"/>
/// and report it as canceled via <see cref="ModelIngestionResource.FailWithCancel"/>
/// See "cooperative cancellation pattern"<br/>
/// Model Ingestion API is available for server versions <c>3.0.3-alpha.583</c> and above
/// See "cooperative cancellation pattern"
/// </remarks>
/// <seealso cref="FailWithError"/>
/// <seealso cref="Complete"/>
@@ -1,2 +1,2 @@
schema: https://app.speckle.systems/graphql
schema: http://localhost/graphql
documents: '**/*.graphql'
+1 -1
View File
@@ -1,2 +1,2 @@
schema: https://app.speckle.systems/graphql
schema: http://localhost/graphql
documents: '**/*.graphql'
@@ -2,7 +2,7 @@
"Type": "AggregateException",
"InnerException": {
"Data": {},
"Message": "NOT_FOUND_ERROR: Model ingestion not found",
"Message": "NOT_FOUND_ERROR: Model ingestion was not found",
"Type": "SpeckleGraphQLException"
}
}
@@ -2,7 +2,7 @@
"Type": "AggregateException",
"InnerException": {
"Data": {},
"Message": "NOT_FOUND_ERROR: Model ingestion not found",
"Message": "NOT_FOUND_ERROR: Model ingestion was not found",
"Type": "SpeckleGraphQLException"
}
}
@@ -1,4 +1,4 @@
using System.Reflection;
using System.Reflection;
using Speckle.Sdk.Api;
using Speckle.Sdk.Api.GraphQL.Inputs;
using Speckle.Sdk.Api.GraphQL.Models;
@@ -8,7 +8,6 @@ using Speckle.Sdk.Models;
namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources;
[Trait("Server", "Internal")]
public sealed class ModelIngestionResourceExceptionalTests : IAsyncLifetime
{
private IClient _testUser;
@@ -12,7 +12,6 @@ using Version = Speckle.Sdk.Api.GraphQL.Models.Version;
namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources;
[Trait("Server", "Internal")]
public sealed class ModelIngestionResourceTests : IAsyncLifetime
{
private IClient _testUser;
@@ -136,42 +135,4 @@ public sealed class ModelIngestionResourceTests : IAsyncLifetime
Assert.Equal(version.id, versionId);
Assert.Equal(sendResult.RootId, version.referencedObject);
}
[Fact]
public async Task TestRequeue()
{
//Not sure if is desirable that ingestions created by the modelIngestionMutations.create mutation can be re-queued
//But the server allows it, so we test it
var createInput = new ModelIngestionCreateInput(
_model.id,
_project.id,
"Starting processing",
new(".NET test runner", "0.0.0", null, null)
);
var ingestion = await Sut.Create(createInput);
var res = await Sut.Requeue(new(ingestion.id, _project.id, "we'll try and requeue this ingestion"));
Assert.Equal(ingestion.id, res.id);
Assert.Equal(ModelIngestionStatus.queued, res.statusData.status);
}
[Fact]
public async Task TestStartProcessing()
{
//Not sure if is desirable that StartProcessing can be used by ingestions created by the modelIngestionMutations.create mutation
//But the server allows it, so we test it
var createInput = new ModelIngestionCreateInput(
_model.id,
_project.id,
"Starting processing",
new(".NET test runner", "0.0.0", null, null)
);
var ingestion = await Sut.Create(createInput);
var res = await Sut.StartProcessing(
new(ingestion.id, _project.id, "", new SourceDataInput("what", "happens", "now", 0))
);
Assert.Equal(ingestion.id, res.id);
Assert.Equal(ModelIngestionStatus.processing, res.statusData.status);
}
}
@@ -1,4 +1,4 @@
using FluentAssertions;
using FluentAssertions;
using Speckle.Sdk.Api;
using Speckle.Sdk.Api.GraphQL.Enums;
using Speckle.Sdk.Api.GraphQL.Inputs;
@@ -136,7 +136,7 @@ public class SubscriptionResourceTests : IAsyncLifetime
subscriptionMessage.comment.Should().NotBeNull();
}
[Fact(Timeout = TIMEOUT), Trait("Server", "Internal")]
[Fact(Timeout = TIMEOUT)]
public async Task ProjectModelIngestionCancellationRequested_SubscriptionIsCalled()
{
ModelIngestion ingestion = await _testUser.Ingestion.Create(
@@ -157,52 +157,4 @@ public class SubscriptionResourceTests : IAsyncLifetime
subscriptionMessage.type.Should().Be(ProjectModelIngestionUpdatedMessageType.cancellationRequested);
subscriptionMessage.modelIngestion.id.Should().Be(ingestion.id);
}
[Fact(Timeout = TIMEOUT), Trait("Server", "Internal")]
public async Task ProjectModelIngestionUpdate_UpdateSubscriptionIs()
{
ModelIngestion ingestion = await _testUser.Ingestion.Create(
new(_testModel.id, _testProject.id, "", new(".NET test", "0.0.0", null, null))
);
TaskCompletionSource<ProjectModelIngestionUpdatedMessage> tcs = new();
using var sub = Sut.CreateProjectModelIngestionUpdatedSubscription(
new(
_testProject.id,
new ModelIngestionReference(ingestion.id, null),
ProjectModelIngestionUpdatedMessageType.updated
)
);
sub.Listeners += (_, message) => tcs.SetResult(message);
await Task.Delay(WAIT_PERIOD); // Give time to subscription to be setup
await _testUser.Ingestion.UpdateProgress(new(ingestion.id, _testProject.id, "Here's an update", 0.314));
var subscriptionMessage = await tcs.Task;
subscriptionMessage.Should().NotBeNull();
subscriptionMessage.type.Should().Be(ProjectModelIngestionUpdatedMessageType.updated);
subscriptionMessage.modelIngestion.id.Should().Be(ingestion.id);
}
[Fact(Timeout = TIMEOUT), Trait("Server", "Internal")]
public async Task ProjectModelIngestionUpdate_CancelSubscriptionIsNotCalled()
{
ModelIngestion ingestion = await _testUser.Ingestion.Create(
new(_testModel.id, _testProject.id, "", new(".NET test", "0.0.0", null, null))
);
TaskCompletionSource<ProjectModelIngestionUpdatedMessage> tcs = new();
using var sub = Sut.CreateProjectModelIngestionCancellationRequestedSubscription(ingestion.id, _testProject.id);
sub.Listeners += (_, message) => tcs.SetResult(message);
await Task.Delay(WAIT_PERIOD); // Give time to subscription to be setup
await _testUser.Ingestion.UpdateProgress(new(ingestion.id, _testProject.id, "this shouldn't cancel", null));
await Task.Delay(WAIT_PERIOD); // Give time to subscription to maybe fire
tcs.Task.IsCompleted.Should().BeFalse();
}
}
@@ -21,7 +21,7 @@ namespace Speckle.Sdk.Tests.Integration;
public static class Fixtures
{
public static readonly ServerInfo Server = new() { url = "http://localhost:3000", name = "Docker Server" };
public static readonly ServerInfo Server = new() { url = "http://localhost", name = "Docker Server" };
public static IServiceProvider ServiceProvider { get; set; }