Skip to content

Commit 922a6c0

Browse files
committed
Initial Commit.
1 parent d3a295e commit 922a6c0

13 files changed

Lines changed: 403 additions & 11 deletions

.github/FUNDING.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# These are supported funding model platforms
2+
3+
ko_fi: tensiondev

.github/dependabot.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# To get started with Dependabot version updates, you'll need to specify which
2+
# package ecosystems to update and where the package manifests are located.
3+
# Please see the documentation for all configuration options:
4+
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5+
6+
version: 2
7+
updates:
8+
- package-ecosystem: "nuget" # See documentation for possible values
9+
directory: "/" # Location of package manifests
10+
schedule:
11+
interval: "monthly"

.github/workflows/dotnet.yml

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
name: .NET
2+
3+
on:
4+
push:
5+
branches: [ "**" ]
6+
pull_request:
7+
branches: [ "master" ]
8+
9+
jobs:
10+
build:
11+
12+
runs-on: windows-latest
13+
14+
strategy:
15+
matrix:
16+
dotnet: [ '8.0.x' ]
17+
name: .NET ${{ matrix.dotnet }}
18+
19+
steps:
20+
- name: Set up JDK 17
21+
uses: actions/setup-java@v4
22+
with:
23+
java-version: 17
24+
distribution: 'zulu' # Alternative distribution options are available.
25+
- uses: actions/checkout@v4
26+
with:
27+
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
28+
- name: Setup .NET
29+
uses: actions/setup-dotnet@v4
30+
with:
31+
dotnet-version: ${{ matrix.dotnet }}
32+
- name: Cache SonarQube Cloud packages
33+
uses: actions/cache@v4
34+
with:
35+
path: ~\sonar\cache
36+
key: ${{ runner.os }}-sonar
37+
restore-keys: ${{ runner.os }}-sonar
38+
- name: Cache SonarQube Cloud scanner
39+
id: cache-sonar-scanner
40+
uses: actions/cache@v4
41+
with:
42+
path: .\.sonar\scanner
43+
key: ${{ runner.os }}-sonar-scanner
44+
restore-keys: ${{ runner.os }}-sonar-scanner
45+
- name: Install SonarQube Cloud scanner
46+
if: steps.cache-sonar-scanner.outputs.cache-hit != 'true'
47+
shell: powershell
48+
run: |
49+
New-Item -Path .\.sonar\scanner -ItemType Directory
50+
dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner
51+
- name: Restore dependencies
52+
run: dotnet restore
53+
- name: SonarCloudPrepare
54+
env:
55+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
56+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
57+
run: .\.sonar\scanner\dotnet-sonarscanner begin /k:"TensionDev_ULID.Serialization.JsonNet" /o:"tensiondev" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.scanner.scanAll=false /d:sonar.cs.opencover.reportsPaths=**/coverage.opencover.xml
58+
- name: Build
59+
run: dotnet build --no-restore
60+
- name: Test
61+
run: dotnet test --no-build --verbosity normal --collect "XPlat Code Coverage;Format=opencover"
62+
- name: SonarCloudAnalyze
63+
env:
64+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
65+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
66+
run: .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"
67+
68+
test:
69+
70+
strategy:
71+
matrix:
72+
dotnet: [ '8.0.x' ]
73+
os: [macos-latest, ubuntu-latest, windows-latest]
74+
75+
runs-on: ${{ matrix.os }}
76+
77+
name: .NET ${{ matrix.dotnet }} on ${{ matrix.os }}
78+
79+
steps:
80+
- uses: actions/checkout@v4
81+
with:
82+
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
83+
- name: Setup .NET
84+
uses: actions/setup-dotnet@v4
85+
with:
86+
dotnet-version: ${{ matrix.dotnet }}
87+
- name: Restore dependencies
88+
run: dotnet restore
89+
- name: Build
90+
run: dotnet build --no-restore
91+
- name: Test
92+
run: dotnet test --no-build --verbosity normal --collect "XPlat Code Coverage;Format=opencover"
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# This is a basic workflow to help you get started with Actions
2+
3+
name: Package Release
4+
5+
# Controls when the workflow will run
6+
on:
7+
# Triggers the workflow on push events but only for the tags
8+
push:
9+
tags:
10+
- v[0-9]+.[0-9]+.[0-9]+
11+
- v[0-9]+.[0-9]+.[0-9]+-alpha
12+
- v[0-9]+.[0-9]+.[0-9]+-beta
13+
14+
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
15+
jobs:
16+
# This workflow contains a single job called "build"
17+
build:
18+
# The type of runner that the job will run on
19+
runs-on: ubuntu-latest
20+
21+
# Steps represent a sequence of tasks that will be executed as part of the job
22+
steps:
23+
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
24+
- uses: actions/checkout@v4
25+
26+
- name: Setup .NET 8
27+
uses: actions/setup-dotnet@v3
28+
with:
29+
dotnet-version: 8.0.x
30+
source-url: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json
31+
env:
32+
NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
33+
34+
- name: Restore dependencies
35+
run: dotnet restore
36+
37+
- name: Build
38+
run: dotnet build --no-restore
39+
40+
- name: Build Release
41+
run: dotnet build --configuration Release --no-restore
42+
43+
- name: Pack
44+
run: dotnet pack --configuration Release
45+
46+
- name: Push NuGet
47+
run: dotnet nuget push "TensionDev.ULID.Serialization.JsonNet/bin/Release/*.nupkg" --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_TOKEN }} --no-symbols --skip-duplicate

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# TensionDev.ULID.Serialization.JsonNet
2+
3+
# Changelog
4+
All notable changes to this project will be documented in this file.
5+
6+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
7+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8+
9+
## [Unreleased]
File renamed without changes.

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
1-
# TensionDev.ULID.Serialization.JsonNet
1+
# TensionDev.ULID.Serialization.JsonNet
2+
3+
[![.NET](https://github.com/TensionDev/ULID.Serialization.JsonNet/actions/workflows/dotnet.yml/badge.svg)](https://github.com/TensionDev/ULID.Serialization.JsonNet/actions/workflows/dotnet.yml)
4+
[![Package Release](https://github.com/TensionDev/ULID.Serialization.JsonNet/actions/workflows/package-release.yml/badge.svg)](https://github.com/TensionDev/ULID.Serialization.JsonNet/actions/workflows/package-release.yml)
5+
[![CodeQL](https://github.com/TensionDev/ULID.Serialization.JsonNet/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/TensionDev/ULID.Serialization.JsonNet/actions/workflows/github-code-scanning/codeql)
6+
7+
TensionDev.ULID.Serialization.JsonNet is a .NET library for serializing and deserializing with Universally Unique Lexicographically Sortable Identifiers (ULIDs) using Json.NET.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
8+
<IsPackable>false</IsPackable>
9+
<IsTestProject>true</IsTestProject>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
14+
<PackageReference Include="Moq" Version="4.20.72" />
15+
<PackageReference Include="xunit" Version="2.9.3" />
16+
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
17+
<PrivateAssets>all</PrivateAssets>
18+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
19+
</PackageReference>
20+
<PackageReference Include="coverlet.collector" Version="8.0.0">
21+
<PrivateAssets>all</PrivateAssets>
22+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
23+
</PackageReference>
24+
</ItemGroup>
25+
26+
<ItemGroup>
27+
<ProjectReference Include="..\TensionDev.ULID.Serialization.JsonNet\TensionDev.ULID.Serialization.JsonNet.csproj" />
28+
</ItemGroup>
29+
30+
</Project>
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
using Moq;
2+
using Newtonsoft.Json;
3+
using Xunit;
4+
5+
namespace TensionDev.ULID.Serialization.JsonNet.Tests
6+
{
7+
public class UlidJsonNetConverterTests : IDisposable
8+
{
9+
private bool disposedValue;
10+
11+
private readonly UlidJsonNetConverter _converter;
12+
13+
public UlidJsonNetConverterTests()
14+
{
15+
_converter = new UlidJsonNetConverter();
16+
}
17+
18+
[Fact]
19+
public void TestReadJson()
20+
{
21+
// Arrange
22+
const string validUlidString = "00000000000000000000000000";
23+
var readerMock = new Mock<JsonReader>(MockBehavior.Strict);
24+
readerMock.SetupGet(r => r.TokenType).Returns(JsonToken.String);
25+
readerMock.SetupGet(r => r.Value).Returns((object)validUlidString);
26+
27+
var converter = new UlidJsonNetConverter();
28+
29+
// existingValue must be non-nullable; obtain an instance via Parse to satisfy parameter constraints.
30+
Ulid existingValue = Ulid.Parse(validUlidString);
31+
var serializer = new JsonSerializer();
32+
33+
// Act
34+
Ulid result = converter.ReadJson(readerMock.Object, typeof(Ulid), existingValue, hasExistingValue: false, serializer: serializer);
35+
36+
// Assert
37+
Assert.NotNull(result);
38+
Assert.Equal(validUlidString, result.ToString());
39+
}
40+
41+
[Fact]
42+
public void TestWriteJson()
43+
{
44+
var writerMock = new Mock<JsonWriter>(MockBehavior.Strict);
45+
object? capturedValue = null;
46+
// Setup to accept any object and capture it
47+
writerMock.Setup(w => w.WriteValue(It.IsAny<string?>()))
48+
.Callback<string?>(v => capturedValue = v)
49+
.Verifiable();
50+
51+
var serializerMock = new Mock<JsonSerializer>(MockBehavior.Loose);
52+
53+
// Attempt to create an instance of the Ulid type.
54+
// This will succeed for structs and for classes with a public parameterless constructor.
55+
// If it fails, mark test inconclusive because we cannot safely fabricate the dependency.
56+
TensionDev.ULID.Ulid value;
57+
try
58+
{
59+
var instance = Activator.CreateInstance(typeof(TensionDev.ULID.Ulid));
60+
if (instance == null)
61+
{
62+
// This can happen if the type is a non-instantiable class (abstract) or Activator returns null.
63+
Assert.Fail("Could not create an instance of TensionDev.ULID.Ulid (Activator.CreateInstance returned null). Provide a parameterless constructor or update the test environment.");
64+
return;
65+
}
66+
67+
value = (TensionDev.ULID.Ulid)instance;
68+
}
69+
catch (Exception ex)
70+
{
71+
// xUnit does not provide Assert.Inconclusive; use Assert.Fail to report the diagnostic and stop the test.
72+
Assert.Fail("Could not create an instance of TensionDev.ULID.Ulid: " + ex.Message);
73+
return;
74+
}
75+
76+
// Act
77+
_converter.WriteJson(writerMock.Object, value, serializerMock.Object);
78+
79+
// Assert
80+
writerMock.Verify(w => w.WriteValue(It.IsAny<string?>()), Times.Once, "WriteValue should be called exactly once.");
81+
// Ensure the captured value matches the value.ToString() result
82+
var expected = value.ToString();
83+
Assert.NotNull(capturedValue);
84+
Assert.Equal(expected, capturedValue?.ToString());
85+
}
86+
87+
protected virtual void Dispose(bool disposing)
88+
{
89+
if (!disposedValue)
90+
{
91+
if (disposing)
92+
{
93+
// TODO: dispose managed state (managed objects)
94+
}
95+
96+
// TODO: free unmanaged resources (unmanaged objects) and override finalizer
97+
// TODO: set large fields to null
98+
disposedValue = true;
99+
}
100+
}
101+
102+
// // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
103+
// ~UlidJsonNetConverterTests()
104+
// {
105+
// // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
106+
// Dispose(disposing: false);
107+
// }
108+
109+
public void Dispose()
110+
{
111+
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
112+
Dispose(disposing: true);
113+
GC.SuppressFinalize(this);
114+
}
115+
}
116+
}

TensionDev.ULID.Serialization.JsonNet.sln

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio Version 18
4-
VisualStudioVersion = 18.3.11520.95 d18.3
4+
VisualStudioVersion = 18.3.11520.95
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TensionDev.ULID.Serialization.JsonNet", "TensionDev.ULID.Serialization.JsonNet\TensionDev.ULID.Serialization.JsonNet.csproj", "{F16EB42F-E09E-424D-9E7A-F085BCD54DDD}"
77
EndProject
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TensionDev.ULID.Serialization.JsonNet.Tests", "TensionDev.ULID.Serialization.JsonNet.Tests\TensionDev.ULID.Serialization.JsonNet.Tests.csproj", "{6969FF86-9D8D-4A6A-AA6B-D8013D949607}"
9+
EndProject
810
Global
911
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1012
Debug|Any CPU = Debug|Any CPU
@@ -15,6 +17,10 @@ Global
1517
{F16EB42F-E09E-424D-9E7A-F085BCD54DDD}.Debug|Any CPU.Build.0 = Debug|Any CPU
1618
{F16EB42F-E09E-424D-9E7A-F085BCD54DDD}.Release|Any CPU.ActiveCfg = Release|Any CPU
1719
{F16EB42F-E09E-424D-9E7A-F085BCD54DDD}.Release|Any CPU.Build.0 = Release|Any CPU
20+
{6969FF86-9D8D-4A6A-AA6B-D8013D949607}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21+
{6969FF86-9D8D-4A6A-AA6B-D8013D949607}.Debug|Any CPU.Build.0 = Debug|Any CPU
22+
{6969FF86-9D8D-4A6A-AA6B-D8013D949607}.Release|Any CPU.ActiveCfg = Release|Any CPU
23+
{6969FF86-9D8D-4A6A-AA6B-D8013D949607}.Release|Any CPU.Build.0 = Release|Any CPU
1824
EndGlobalSection
1925
GlobalSection(SolutionProperties) = preSolution
2026
HideSolutionNode = FALSE

0 commit comments

Comments
 (0)