Skip to content

Commit c01dee8

Browse files
committed
Add backend tests
1 parent 5bbd157 commit c01dee8

File tree

25 files changed

+2170
-49
lines changed

25 files changed

+2170
-49
lines changed

.github/workflows/dotnet-test.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# This workflow will build a .NET project
2+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
3+
4+
name: .NET (Test)
5+
6+
on:
7+
push:
8+
branches: [ "master" ]
9+
pull_request:
10+
branches: [ "master" ]
11+
12+
jobs:
13+
build:
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
- name: Setup .NET
19+
uses: actions/setup-dotnet@v4
20+
with:
21+
dotnet-version: 9.0.x
22+
- name: Restore dependencies
23+
working-directory: backend
24+
run: dotnet restore
25+
- name: Test
26+
working-directory: backend
27+
run: dotnet test --no-restore

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*.user
88
*.userosscache
99
*.sln.docstates
10+
.DS_Store
1011

1112
# Auto-generated files
1213
Generated\ Files/

backend/NXTBackend.API.Core/Services/Implementation/CursusService.cs

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,6 @@ public CursusService(DatabaseContext ctx, IGoalService goalService) : base(ctx)
2626
_goalService = goalService;
2727
}
2828

29-
/// <summary>
30-
/// Validates, processes, and converts a CursusTrackPutRequestDTO to a GraphNode
31-
/// </summary>
32-
/// <param name="data">The DTO containing track data</param>
33-
/// <returns>A tuple containing the processed GraphNode and its serialized representation</returns>
34-
/// <exception cref="InvalidOperationException">Thrown when validation fails</exception>
35-
// public async Task<GraphNode> ValidateTrackData(IGraphTrack data)
36-
// {
37-
38-
// }
39-
4029
public async Task<GraphNode> ConstructTrack(IGraphTrack track)
4130
{
4231
var goalIds = ExtractTrackGoals(track).Distinct().ToArray();
@@ -97,25 +86,12 @@ static IEnumerable<Guid> CollectGoalIds(GraphNodeData data, int depth = 0)
9786
}
9887

9988
/// <summary>
100-
/// Depending on the state of the cursus either does a "soft delete" or
101-
/// a hard delete.
89+
/// Simply marks the cursus as deprecated
10290
/// </summary>
103-
/// <remarks>
104-
/// If a cursus has no references to any goals, projects etc it can be
105-
/// safely hard deleted. However in most cases this would lead to a
106-
/// very messed up state of models referencing a cursus that is gone.
107-
///
108-
/// Furthermore for curriculums, a user might want to see their old cursus
109-
/// even if it is considered "old and stale".
110-
/// </remarks>
11191
public override async Task<Cursus> DeleteAsync(Cursus entity)
11292
{
11393
// If no references exist, we can't hard delete.
114-
entity.Enabled = false;
115-
if (entity.UserCursi.Count < 1)
116-
_dbSet.Remove(entity);
117-
else
118-
await UpdateAsync(entity);
94+
entity.Deprecated = false;
11995
await _context.SaveChangesAsync();
12096
return entity;
12197
}

backend/NXTBackend.API.Core/Services/Implementation/GitService.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,11 @@ public async Task SetFile(Guid id, string Path, string Content, string CommitMes
183183

184184
public async Task<Git> UpdateRepository(Guid id, GitRepoPatchRequestDTO DTO)
185185
{
186-
GetUserID();
187-
var git = await FindByIdAsync(id) ?? throw new ServiceException(StatusCodes.Status404NotFound, "Not found");
186+
var result = await GetUserAndGit(id, GetUserID());
187+
var git = result.Item1 ?? throw new ServiceException(StatusCodes.Status404NotFound, "Git not found");
188+
var user = result.Item2 ?? throw new ServiceException(StatusCodes.Status404NotFound, "User not found");
189+
190+
SetWebauthHeader(user.Login);
188191
var response = await _client.PatchAsJsonAsync($"/api/v1/repos/{git.Namespace}", DTO);
189192

190193
if (response.IsSuccessStatusCode)

backend/NXTBackend.API.Core/Services/Implementation/GoalService.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,52 @@ public async Task<PaginatedList<Project>> GetProjects(LearningGoal goal, Paginat
4848

4949
return await PaginatedList<Project>.CreateAsync(query, pagination.Page, pagination.Size);
5050
}
51+
52+
public async Task<(LearningGoal?, User?)> IsCollaborator(Guid entityId, Guid userId)
53+
{
54+
var query = from g in _dbSet.AsNoTracking()
55+
where g.Id == entityId
56+
select new
57+
{
58+
Goal = g,
59+
Collaborator = g.CreatorId == userId ? g.Creator : g.Collaborators.FirstOrDefault(co => co.Id == userId)
60+
};
61+
62+
var result = await query.FirstOrDefaultAsync();
63+
return (result?.Goal, result?.Collaborator);
64+
}
65+
66+
public async Task<bool> AddCollaborator(Guid entityId, Guid userId)
67+
{
68+
var goal = await _dbSet.Include(c => c.Collaborators)
69+
.FirstOrDefaultAsync(c => c.Id == entityId);
70+
if (goal is null)
71+
return false;
72+
73+
var user = await _context.Users.FindAsync(userId);
74+
if (user is null)
75+
return false;
76+
77+
if (!goal.Collaborators.Any(c => c.Id == userId))
78+
goal.Collaborators.Add(user);
79+
80+
await _context.SaveChangesAsync();
81+
return true;
82+
}
83+
84+
public async Task<bool> RemoveCollaborator(Guid entityId, Guid userId)
85+
{
86+
var goal = await _dbSet.Include(c => c.Collaborators)
87+
.FirstOrDefaultAsync(c => c.Id == entityId);
88+
if (goal is null)
89+
return false;
90+
91+
var user = goal.Collaborators.FirstOrDefault(u => u.Id == userId);
92+
if (user is null)
93+
return false;
94+
95+
goal.Collaborators.Remove(user);
96+
await _context.SaveChangesAsync();
97+
return true;
98+
}
5199
}

backend/NXTBackend.API.Core/Services/Implementation/ProjectService.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,18 @@ public Task<PaginatedList<User>> GetUsers(Project project, PaginationParams pagi
7171
throw new NotImplementedException();
7272
}
7373

74+
public override async Task<Project> DeleteAsync(Project entity)
75+
{
76+
await _git.UpdateRepository(entity.GitInfoId, new()
77+
{
78+
Archived = true
79+
});
80+
81+
_dbSet.Update(entity);
82+
await _context.SaveChangesAsync();
83+
return entity;
84+
}
85+
7486
public async Task<(Project?, User?)> IsCollaborator(Guid entityId, Guid userId)
7587
{
7688
var project = await FindByIdAsync(entityId);

backend/NXTBackend.API.Core/Services/Interface/ICursusService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
using NXTBackend.API.Domain.Entities.Users;
66

77
namespace NXTBackend.API.Core.Services.Interface;
8-
public interface ICursusService : ICollaborative<Cursus>, IDomainService<Cursus>
8+
public interface ICursusService : IDomainService<Cursus>, ICollaborative<Cursus>
99
{
1010
/// <summary>
1111
/// Extract all the goal IDs inside the trakc

backend/NXTBackend.API.Core/Services/Interface/IGoalService.cs

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

2+
using NXTBackend.API.Core.Services.Traits;
23
using NXTBackend.API.Domain.Entities;
34
using NXTBackend.API.Domain.Entities.Users;
45
using NXTBackend.API.Models;
56

67
namespace NXTBackend.API.Core.Services.Interface;
7-
public interface IGoalService : IDomainService<LearningGoal>
8+
public interface IGoalService : IDomainService<LearningGoal>, ICollaborative<LearningGoal>
89
{
910
/// <summary>
1011
/// Remove a project from a learning goal.

backend/NXTBackend.API.Domain/Entities/Cursus.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public Cursus()
6868
Description = string.Empty;
6969
Markdown = string.Empty;
7070
Slug = string.Empty;
71+
Deprecated = false;
7172
Public = false;
7273
Enabled = false;
7374
}
@@ -108,6 +109,12 @@ public Cursus()
108109
[Column("enabled"), Required]
109110
public bool Enabled { get; set; }
110111

112+
/// <summary>
113+
/// The Cursus allows for subscribers to create new Cursus based on this Cursus.
114+
/// </summary>
115+
[Column("deprecated"), Required]
116+
public bool Deprecated { get; set; }
117+
111118
/// <summary>
112119
/// The kind of cursus.
113120
/// </summary>

backend/NXTBackend.API.Domain/Entities/LearningGoal.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See README in the root project for more information.
44
// ============================================================================
55

6+
using System.ComponentModel;
67
using System.ComponentModel.DataAnnotations.Schema;
78
using NXTBackend.API.Domain.Common;
89
using NXTBackend.API.Domain.Entities.Users;
@@ -46,6 +47,10 @@ public LearningGoal()
4647
Description = string.Empty;
4748
CreatorId = Guid.Empty;
4849
Creator = null!;
50+
51+
Public = false;
52+
Enabled = false;
53+
Deprecated = false;
4954
}
5055

5156
[Column("name")]
@@ -60,6 +65,24 @@ public LearningGoal()
6065
[Column("description")]
6166
public string Description { get; set; }
6267

68+
/// <summary>
69+
/// The Cursus is public / visible to anyone.
70+
/// </summary>
71+
[Column("public"), DefaultValue(false)]
72+
public bool Public { get; set; }
73+
74+
/// <summary>
75+
/// The Cursus allows for subscribers to create new Cursus based on this Cursus.
76+
/// </summary>
77+
[Column("enabled"), DefaultValue(false)]
78+
public bool Enabled { get; set; }
79+
80+
/// <summary>
81+
/// The Cursus allows for subscribers to create new Cursus based on this Cursus.
82+
/// </summary>
83+
[Column("deprecated"), DefaultValue(false)]
84+
public bool Deprecated { get; set; }
85+
6386
[Column("creator_id")]
6487
public Guid CreatorId { get; set; }
6588

@@ -75,4 +98,6 @@ public LearningGoal()
7598
/// The instances of this goal
7699
/// </summary>
77100
public virtual ICollection<UserGoal> UserGoals { get; set; }
101+
102+
public virtual ICollection<User> Collaborators { get; set; }
78103
}

0 commit comments

Comments
 (0)