Skip to content
This repository has been archived by the owner on Sep 4, 2023. It is now read-only.

Commit

Permalink
Feat/58 unit testing poc (#73)
Browse files Browse the repository at this point in the history
* Release v1.0.0 (#41)

* Change formats type to `string` and split with comma separator

* Use root configuration instead of configuration section

* Fix exceptions not properly displaying in the console

* Add all module item types according to documentation

* Update application demo GIF

* Create code-analysis.yml

* Remove comments and enable additional queries

* Removed push from code-analysis.yml workflow

* Typo code-analysis.yml

* Added support for a Word export option

* Added footer Epsilon Credits

* Created helper class with reformat functions

* Reformat code

* Improved helper functions, Found that they can be added to records :-)

* Reformat files

* string interpolation

* Feature/cleanup (#40)

* Remove obsolete Epsilon.Http.Abstractions project

* Fix invalid serializable implementation

* Disable unused method return value hint

* Change exception to more appropriate one

* Update Grade score description

* Prevent null grades from exporting

* Reduce cognitive complexity to acceptable level

* Move logging call

* Reduce nesting

* Move project name and repository uri to constants

* Fix nullability warnings

* Remove unused class

* Use ?: operator and move constants to top level of class

* Reduce loop complexity

* Use project name constant in output name export option

* Update README.md application demo gif

* Add supported formats to README.md

Co-authored-by: Jelle Maas <typiqally@gmail.com>

* POC Open XML SDK

* Update

* Add KPI format and structure

* Added module names to document

* Testing the build and structure of exel files.

* Testing release deployment script.

* Delete file

* POC for excel export

* Code reformat

* Abstracted format logic from exporters to main logic

* Remove typo

* Remove unused variable

* Headers added

* Code cleanup

* Merge

* Removal nullability warnings

* Removal nullability warnings

* Removal tasks/awaits because they dont have nay use atm.

* Removal tasks/awaits because they dont have nay use atm.

* Add exporters return type

* Added memoryStream return type for ConsoleModuleExporter

* Remove unused ExportOptions from ConsoleModuleExporter

* Clean up old merge files

* Update continuous-integration.yml

* Add tests for HttpService, Exporter and ExportDataPackager

* Update continuous-integration.yml

* 'Var' is everything

* Update continuous-integration.yml

* Created unit test for CanvasModuleCollectionFetcher GetAll method

* Renamed unit test

* Bugfix

* Merged feat/42 into feat/58

* Refactored ExcelModuleExporter tests to WordModuleExporter tests

---------

Co-authored-by: Neal Geilen <info@nealgeilen.nl>
Co-authored-by: Jelle Maas <typiqally@gmail.com>
Co-authored-by: Sven Hansen <76601644+1SvenHansen@users.noreply.github.com>
  • Loading branch information
4 people committed May 26, 2023
1 parent 6de73b2 commit 665a59a
Show file tree
Hide file tree
Showing 17 changed files with 427 additions and 30 deletions.
30 changes: 25 additions & 5 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,36 @@
on: [ pull_request ]

jobs:
build:
name: Build
setup:
name: Setup
runs-on: ubuntu-20.04

steps:
- uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1.7.2

- name: Setup .NET
uses: actions/setup-dotnet@v1.7.2
with:
dotnet-version: '6.0.x'

build:
name: Build
needs: [ setup ]
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2

- name: Restore dependencies
run: dotnet restore

- name: Build
run: dotnet build --no-restore
run: dotnet build --no-restore

test:
name: Test
needs: [ setup, build ]
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2

- name: Test
run: dotnet test --no-build --verbosity normal
2 changes: 0 additions & 2 deletions Epsilon.Abstractions/Export/IExporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
public interface IExporter<in T>
{
public IEnumerable<string> Formats { get; }

public string FileExtension { get; }

Task<Stream> Export(T data, string format);
}
2 changes: 1 addition & 1 deletion Epsilon.Abstractions/Model/CourseModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
public class CourseModule
{
public string Name { get; set; } = String.Empty;
public IEnumerable<CourseOutcome> Kpis { get; set; } = Enumerable.Empty<CourseOutcome>();
public IEnumerable<CourseOutcome> Outcomes { get; set; } = Enumerable.Empty<CourseOutcome>();
}
}
126 changes: 126 additions & 0 deletions Epsilon.Canvas.Tests/CanvasModuleCollectionFetcherTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
using Epsilon.Abstractions.Model;
using Epsilon.Canvas.Abstractions.Model;
using Epsilon.Canvas.Abstractions.Service;
using Microsoft.Extensions.Logging;
using Moq;
using System.Text.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;
using Epsilon.Canvas.Abstractions;

namespace Epsilon.Canvas.Tests
{
public class CanvasModuleCollectionFetcherTests
{
[Fact]
public async Task GivenAllowedModulesAndCourseId_WhenModuleAndOutcomeServiceAreMocked_ThenReturnsExpectedModuleOutcomeResultCollection()
{
// Arrange
var courseId = 123;
var allowedModules = new[] { "Module 1", "Module 3" };
var outcomeServiceMock = new Mock<IOutcomeHttpService>();
var moduleServiceMock = new Mock<IModuleHttpService>();

var expectedResults = new List<ModuleOutcomeResultCollection>
{
new ModuleOutcomeResultCollection(
new Module(1, "Module 1", 3, new List<ModuleItem>
{
new ModuleItem(1, "Module 1 Item 1", ModuleItemType.Page, 1),
new ModuleItem(2, "Module 1 Item 2", ModuleItemType.Assignment, 2),
new ModuleItem(3, "Module 1 Item 3", ModuleItemType.Quiz, 3)
}),
new OutcomeResultCollection(
new List<OutcomeResult> { },
new OutcomeResultCollectionLink(
new List<Outcome>
{
new Outcome(1, "Outcome 1", "Outcome 1 EN Short Description NL Long Description"),
new Outcome(2, "Outcome 2", "Outcome 2 EN Short Description NL Long Description")
},
new List<Alignment> { }
)
)
),
new ModuleOutcomeResultCollection(
new Module(3, "Module 3", 2, new List<ModuleItem>
{
new ModuleItem(4, "Module 3 Item 1", ModuleItemType.Assignment, 4),
new ModuleItem(5, "Module 3 Item 2", ModuleItemType.Assignment, 5)
}),
new OutcomeResultCollection(
new List<OutcomeResult> { },
new OutcomeResultCollectionLink(
new List<Outcome>
{
new Outcome(1, "Outcome 1", "Outcome 1 EN Short Description NL Long Description"),
new Outcome(2, "Outcome 2", "Outcome 2 EN Short Description NL Long Description")
},
new List<Alignment> { }
)
)
)
};

outcomeServiceMock
.Setup(s => s.GetResults(It.IsAny<int>(), It.IsAny<string[]>()))
.ReturnsAsync(new OutcomeResultCollection(
new List<OutcomeResult>
{
new OutcomeResult(false, 3, new OutcomeResultLink("user1", "1", "1", "2")),
new OutcomeResult(true, 4.5, new OutcomeResultLink("user2", "2", "2", "3")),
new OutcomeResult(false, null, new OutcomeResultLink("user1", "1", "1", "3")),
},
new OutcomeResultCollectionLink(
new List<Outcome>
{
new Outcome(1, "Outcome 1", "Outcome 1 EN Short Description NL Long Description"),
new Outcome(2, "Outcome 2", "Outcome 2 EN Short Description NL Long Description"),
},
new List<Alignment>
{
new Alignment("1", "Alignment 1", new Uri("https://alignment1.com")),
new Alignment("2", "Alignment 2", new Uri("https://alignment2.com")),
new Alignment("3", "Alignment 3", new Uri("https://alignment3.com")),
}
)
));

moduleServiceMock
.Setup(s => s.GetAll(It.IsAny<int>(), It.IsAny<string[]>()))
.ReturnsAsync(new List<Module>
{
new Module(1, "Module 1", 3, new List<ModuleItem>
{
new ModuleItem(1, "Module 1 Item 1", ModuleItemType.Page, 1),
new ModuleItem(2, "Module 1 Item 2", ModuleItemType.Assignment, 2),
new ModuleItem(3, "Module 1 Item 3", ModuleItemType.Quiz, 3)
}),
new Module(2, "Module 2", 0, new List<ModuleItem>()),
new Module(3, "Module 3", 2, new List<ModuleItem>
{
new ModuleItem(4, "Module 3 Item 1", ModuleItemType.Assignment, 4),
new ModuleItem(5, "Module 3 Item 2", ModuleItemType.Assignment, 5)
}),
});

var canvasModuleCollectionFetcher = new CanvasModuleCollectionFetcher(
Mock.Of<ILogger<CanvasModuleCollectionFetcher>>(), moduleServiceMock.Object, outcomeServiceMock.Object
);

// Act
var result = new List<ModuleOutcomeResultCollection>();
await foreach (var item in canvasModuleCollectionFetcher.GetAll(courseId, allowedModules))
{
result.Add(item);
}

// Assert
Assert.Equal(JsonSerializer.Serialize(expectedResults), JsonSerializer.Serialize(result));
}
}
}
8 changes: 6 additions & 2 deletions Epsilon.Canvas.Tests/Epsilon.Canvas.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Include="coverlet.collector" Version="3.1.0" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
Expand Down
52 changes: 52 additions & 0 deletions Epsilon.Canvas.Tests/Services/AssignmentHttpServiceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading;
using System.Threading.Tasks;
using Epsilon.Abstractions.Http;
using Epsilon.Canvas.Abstractions.Model;
using Epsilon.Canvas.Abstractions.Service;
using Moq;
using Moq.Protected;
using Xunit;

namespace Epsilon.Canvas.Service.Tests
{
public class AssignmentHttpServiceTests
{
private readonly HttpClient _httpClient;
private readonly Mock<IPaginatorHttpService> _paginatorHttpServiceMock;
private readonly AssignmentHttpService _assignmentHttpService;

public AssignmentHttpServiceTests()
{
_httpClient = new HttpClient();
_paginatorHttpServiceMock = new Mock<IPaginatorHttpService>();
_assignmentHttpService = new AssignmentHttpService(_httpClient, _paginatorHttpServiceMock.Object);
}

[Fact]
public async Task GivenCourseIdAndIncludeSubmissionAndRubricAssessment_WhenCanvasAssignmentsAreRetrieved_ThenAssignmentsAreReturned()
{
// Arrange
var courseId = 123;
var include = new[] { "submission", "rubric_assessment" };
var assignments = new[]
{
new Assignment(1, "Assignment 1", new("https://example.com/1"), null),
new Assignment(2, "Assignment 2", new("https://example.com/2"), new Submission(null, null, null, null)),
new Assignment(3, "Assignment 3", new("https://example.com/3"), new Submission(null, null, new RubricAssessment(8.5, 1, Enumerable.Empty<RubricRating>()), null)),
};
_paginatorHttpServiceMock.Setup(x => x.GetAllPages<IEnumerable<Assignment>>(HttpMethod.Get, $"v1/courses/{courseId}/assignments?include[]=submission&include[]=rubric_assessment"))
.ReturnsAsync(new[] { assignments });

// Act
var result = await _assignmentHttpService.GetAll(courseId, include);

// Assert
Assert.Equal(assignments, result);
}
}
}
1 change: 1 addition & 0 deletions Epsilon.Cli/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ private async Task ExecuteAsync()
foreach (var (format, exporter) in exporters)
{
_logger.LogInformation("Exporting to {Format} using {Exporter}...", format, exporter.GetType().Name);

var stream = await exporter.Export(formattedItems, format);

await using var fileStream = new FileStream($"{_exportOptions.FormattedOutputName}.{exporter.FileExtension}", FileMode.Create, FileAccess.Write);
Expand Down
30 changes: 30 additions & 0 deletions Epsilon.Tests/Epsilon.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Epsilon.Abstractions\Epsilon.Abstractions.csproj" />
<ProjectReference Include="..\Epsilon\Epsilon.csproj" />
</ItemGroup>

</Project>
94 changes: 94 additions & 0 deletions Epsilon.Tests/ExportDataPackagerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using Epsilon.Abstractions.Model;
using Epsilon.Canvas.Abstractions.Model;
using Epsilon.Export;
using Moq;
using System.Text.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Epsilon.Tests
{
public class ExportDataPackagerTests
{
[Fact]
public async Task GivenListOfModuleOutcomeResultCollection_WhenRequiringOpenLearningOutcomeStructure_ThenOutcomeStructureShouldBeTransformed()
{
// Arrange
var module = new Module(1, "Module 1", 3, new List<ModuleItem>
{
new ModuleItem(1, "Module 1 Item 1", ModuleItemType.Page, 1),
new ModuleItem(2, "Module 1 Item 2", ModuleItemType.Assignment, 2),
new ModuleItem(3, "Module 1 Item 3", ModuleItemType.Quiz, 3)
});

var outcomes = new List<Outcome>
{
new Outcome(1, "Outcome 1", "Outcome 1 EN Short Description NL Long Description"),
new Outcome(2, "Outcome 2", "Outcome 2 EN Short Description NL Long Description"),
};

var alignments = new List<Alignment>
{
new Alignment("1", "Alignment 1", new Uri("https://alignment1.com")),
new Alignment("2", "Alignment 2", new Uri("https://alignment2.com")),
};

var outcomeResults = new List<OutcomeResult>
{
new OutcomeResult(false, 3, new OutcomeResultLink("user1", "1", "1", "2")),
new OutcomeResult(true, 4.5, new OutcomeResultLink("user2", "2", "2", "3")),
new OutcomeResult(false, null, new OutcomeResultLink("user1", "1", "1", "3")),
};

var links = new OutcomeResultCollectionLink(outcomes, alignments);

var collection = new OutcomeResultCollection(outcomeResults, links);

var moduleOutcomeResultCollection = new ModuleOutcomeResultCollection(module, collection);

var data = new List<ModuleOutcomeResultCollection>
{
moduleOutcomeResultCollection
}.ToAsyncEnumerable();

var expectedData = new ExportData
{
CourseModules = new List<CourseModule>
{
new CourseModule
{
Name = "Module 1",
Outcomes = new List<CourseOutcome>
{
new CourseOutcome
{
Name = "Outcome 1",
Description = "Short Description",
Assignments = new List<CourseAssignment>
{
new CourseAssignment
{
Name = "Alignment 2",
Url = "https://alignment2.com/",
Score = "Satisfactory"
}
}
}
}
}
}
};

var exportDataPackager = new ExportDataPackager();

// Act
var result = await exportDataPackager.GetExportData(data);

// Assert
Assert.Equal(JsonSerializer.Serialize(expectedData), JsonSerializer.Serialize(result));
}
}
}
Loading

0 comments on commit 665a59a

Please sign in to comment.