Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
dcd305b
Merge pull request #1 from amujalo2/dev
amujalo2 May 18, 2025
5890ce6
Add or update the Azure App Service build and deployment workflow config
amujalo2 May 18, 2025
f021cab
fix yml file and added spinner while loading messages
amujalo2 May 18, 2025
3a69387
refactor logic in MessageHub.cs, works the same
amujalo2 May 23, 2025
e1afbce
ApplicationInsights Serilog implementation
amujalo2 May 23, 2025
8ded8a2
Initial commit for Task1
amujalo2 May 27, 2025
729c12b
partial done neads fixing
amujalo2 May 28, 2025
539a094
updated ui for photo editor component and fixed mapper in backend
amujalo2 May 28, 2025
8178ffc
updated ui for photo editor component and fixed mapper in backend
amujalo2 May 28, 2025
65809b0
Task2, .NET TASK finished
amujalo2 May 28, 2025
564414a
Merge pull request #2 from amujalo2/Task2PhotoTagging
amujalo2 May 28, 2025
2b76688
Made Service calsses interfaces for mock
amujalo2 May 29, 2025
0962b48
Added Tests v1
amujalo2 May 29, 2025
ba17b16
Tests Partial Finished
amujalo2 May 29, 2025
ba2cf86
Merge pull request #3 from amujalo2/Task2PhotoTagging
amujalo2 May 29, 2025
20a1c9d
Fixed sql procedure in adminController
amujalo2 May 29, 2025
a35ef4a
Tests Fix, all tests pass
amujalo2 May 30, 2025
81b43db
Merge pull request #4 from amujalo2/Task2PhotoTagging
amujalo2 May 30, 2025
d93dcf3
fix adding procedure functionality for admin
amujalo2 May 30, 2025
94d09dc
fix admin roles procedure fetcher
amujalo2 May 30, 2025
daedcf8
Merge pull request #5 from amujalo2/Task2PhotoTagging
amujalo2 May 30, 2025
621475e
added better frontend, fixed logic in assingningPhotos in backend ser…
amujalo2 May 31, 2025
1b5b8ca
Merge pull request #6 from amujalo2/Task2PhotoTagging
amujalo2 May 31, 2025
43720d3
Fix error handeling in forntend
amujalo2 Jun 4, 2025
cebc86e
Merge pull request #7 from amujalo2/Task2PhotoTagging
amujalo2 Jun 4, 2025
6977bfd
dodao ApplicationInsights u services
amujalo2 Jun 4, 2025
8a0cf03
Merge pull request #8 from amujalo2/Task2PhotoTagging
amujalo2 Jun 4, 2025
46cf577
fix telemetry
amujalo2 Jun 4, 2025
e15feb4
Merge pull request #9 from amujalo2/Task2PhotoTagging
amujalo2 Jun 4, 2025
7dd1905
simple fix
amujalo2 Jun 5, 2025
1de3aa3
Merge pull request #10 from amujalo2/Task2PhotoTagging
amujalo2 Jun 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions .github/workflows/main_dating-app-htec-intern.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions

name: Build and deploy ASP.Net Core app to Azure Web App - Dating-App-HTEC-Intern

on:
push:
branches:
- main
workflow_dispatch:

jobs:
build:
runs-on: windows-latest
permissions:
contents: read #This is required for actions/checkout

steps:
- uses: actions/checkout@v4

- name: Set up node.js
uses: actions/setup-node@v3
with:
node-version: '20'

- name: Install Angular CLI
run: npm install -g @angular/cli@19

- name: Install deps and build angular app
run: |
cd client
npm install
ng build

- name: Set up .NET Core
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.x'

- name: Build with dotnet
run: dotnet build --configuration Release

- name: dotnet publish
run: dotnet publish -c Release -o "${{env.DOTNET_ROOT}}/myapp"

- name: Upload artifact for deployment job
uses: actions/upload-artifact@v4
with:
name: .net-app
path: ${{env.DOTNET_ROOT}}/myapp

deploy:
runs-on: windows-latest
needs: build
environment:
name: 'Production'
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
permissions:
id-token: write #This is required for requesting the JWT
contents: read #This is required for actions/checkout

steps:
- name: Download artifact from build job
uses: actions/download-artifact@v4
with:
name: .net-app

- name: Login to Azure
uses: azure/login@v2
with:
client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_208CA351617B4FA788EAE781A942B3F9 }}
tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID_E47467B042204ABB9220C7F466DA4B13 }}
subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_288A10112D9A4341B4F6DAD43DAEC34F }}

- name: Deploy to Azure Web App
id: deploy-to-webapp
uses: azure/webapps-deploy@v3
with:
app-name: 'Dating-App-HTEC-Intern'
slot-name: 'Production'
package: .

26 changes: 26 additions & 0 deletions API.Tests/API.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\API\API.csproj" />
</ItemGroup>

<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>

</Project>
208 changes: 208 additions & 0 deletions API.Tests/PhotoApprovalTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
using Moq;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Identity;
using API.Interfaces;
using API.Entities;
using API.Services;
using Xunit;
using API.Services._Admin;
using Microsoft.AspNetCore.SignalR;
using API.SignalR;
using API.Errors;

namespace API.Tests
{
public class PhotoApprovalTests
{
private readonly Mock<IUnitOfWork> _unitOfWorkMock;
private readonly Mock<IPhotoRepository> _photoRepoMock;
private readonly Mock<IUserRepository> _userRepoMock;
private readonly Mock<IPhotoService> _photoServiceMock;
private readonly Mock<UserManager<AppUser>> _userManagerMock;
private readonly Mock<IHubContext<PresenceHub>> _hubContext;
private readonly AdminService _adminService;

public PhotoApprovalTests()
{
_unitOfWorkMock = new Mock<IUnitOfWork>();
_photoRepoMock = new Mock<IPhotoRepository>();
_userRepoMock = new Mock<IUserRepository>();
_photoServiceMock = new Mock<IPhotoService>();
_userManagerMock = new Mock<UserManager<AppUser>>(
Mock.Of<IUserStore<AppUser>>(), null, null, null, null, null, null, null, null

Check warning on line 32 in API.Tests/PhotoApprovalTests.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.

Check warning on line 32 in API.Tests/PhotoApprovalTests.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.

Check warning on line 32 in API.Tests/PhotoApprovalTests.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.

Check warning on line 32 in API.Tests/PhotoApprovalTests.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.

Check warning on line 32 in API.Tests/PhotoApprovalTests.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.

Check warning on line 32 in API.Tests/PhotoApprovalTests.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.

Check warning on line 32 in API.Tests/PhotoApprovalTests.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.

Check warning on line 32 in API.Tests/PhotoApprovalTests.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.
);
_hubContext = new Mock<IHubContext<PresenceHub>>();

_unitOfWorkMock.Setup(u => u.PhotoRepository).Returns(_photoRepoMock.Object);
_unitOfWorkMock.Setup(u => u.UserRepository).Returns(_userRepoMock.Object);

_adminService = new AdminService(
_userManagerMock.Object,
_unitOfWorkMock.Object,
_photoServiceMock.Object,
_hubContext.Object
);
}

[Fact]
public async Task ApprovePhotoAsync_ApprovesPhoto_WhenPhotoExists()
{
// Arrange
var appUserId = 2;
var photo = CreateTestPhoto(appUserId: appUserId, isMain: false, isApproved: false, id: 1);
var user = CreateTestUser(appUserId, photo);

SetupMocksForSuccessfulApproval(photo, user);

// Act
await _adminService.ApprovePhoto(photo.Id);

// Assert
Assert.True(photo.IsApproved);
_unitOfWorkMock.Verify(u => u.Complete(), Times.Once);
}

[Fact]
public async Task ApprovePhotoAsync_SetAsMain_WhenNoMainPhotoExists()
{
// Arrange
var appUserId = 2;
var photo = CreateTestPhoto(appUserId: appUserId);
var user = CreateTestUser(appUserId, photo);

SetupMocksForSuccessfulApproval(photo, user);

// Act
await _adminService.ApprovePhoto(photo.Id);

// Assert
Assert.True(photo.IsMain);
Assert.True(photo.IsApproved);
_unitOfWorkMock.Verify(u => u.Complete(), Times.Once);
}

[Fact]
public async Task ApprovePhotoAsync_DoesNotSetAsMain_WhenMainPhotoAlreadyExists()
{
// Arrange
var appUserId = 2;
var mainPhoto = CreateTestPhoto(id: 1, isMain: true, isApproved: true, appUserId: appUserId);
var photoToApprove = CreateTestPhoto(id: 2, isMain: false, isApproved: false, appUserId: appUserId);
var user = CreateTestUser(appUserId, mainPhoto, photoToApprove);

SetupMocksForSuccessfulApproval(photoToApprove, user);

// Act
await _adminService.ApprovePhoto(photoToApprove.Id);

// Assert
Assert.False(photoToApprove.IsMain);
Assert.True(photoToApprove.IsApproved);
Assert.True(mainPhoto.IsMain); // Main photo should remain unchanged
}

[Fact]
public async Task ApprovePhotoAsync_ThrowsNotFoundException_WhenPhotoNotFound()
{
// Arrange
const int nonExistentPhotoId = 999;
_photoRepoMock.Setup(r => r.GetPhotoById(nonExistentPhotoId))
.ReturnsAsync((Photo?)null);

// Act & Assert
await Assert.ThrowsAsync<NotFoundException>(
() => _adminService.ApprovePhoto(nonExistentPhotoId));
_unitOfWorkMock.Verify(u => u.Complete(), Times.Never);
}

[Fact]
public async Task ApprovePhotoAsync_ThrowsNotFoundException_WhenProblemApproving()
{
// Arrange
var appUserId = 2;
var photo = CreateTestPhoto(appUserId: appUserId);
var user = CreateTestUser(appUserId, photo);

_photoRepoMock.Setup(r => r.GetPhotoById(photo.Id)).ReturnsAsync(photo);
_photoRepoMock.Setup(r => r.GetUserByPhotoId(photo.Id)).ReturnsAsync(user);
_unitOfWorkMock.Setup(u => u.Complete()).ReturnsAsync(false); // Simulate failure

// Act & Assert
var ex = await Assert.ThrowsAsync<NotFoundException>(
() => _adminService.ApprovePhoto(photo.Id));
Assert.Equal("Failed to approve photo", ex.Message);
_unitOfWorkMock.Verify(u => u.Complete(), Times.Once); // Promena: Times.Once umesto Times.Never
}

[Theory]
[InlineData(0)]
[InlineData(-1)]
[InlineData(-999)]
public async Task ApprovePhotoAsync_ThrowsNotFoundException_WhenPhotoIdIsInvalid(int invalidId)
{
// Arrange
_photoRepoMock.Setup(r => r.GetPhotoById(invalidId))
.ReturnsAsync((Photo?)null);

// Act & Assert
var exception = await Assert.ThrowsAsync<NotFoundException>(
() => _adminService.ApprovePhoto(invalidId));

Assert.Equal("Photo not found", exception.Message);
}

[Fact]
public async Task ApprovePhotoAsync_ThrowsNotFoundException_WhenUserNotFound()
{
// Arrange
var appUserId = 2;
var photo = CreateTestPhoto(appUserId: appUserId);

_photoRepoMock.Setup(r => r.GetPhotoById(photo.Id)).ReturnsAsync(photo);
_photoRepoMock.Setup(r => r.GetUserByPhotoId(photo.Id))
.ReturnsAsync((AppUser?)null);

// Act & Assert
var ex = await Assert.ThrowsAsync<NotFoundException>(
() => _adminService.ApprovePhoto(photo.Id));
Assert.Equal("User not found", ex.Message);
}

#region Helper Methods

private static Photo CreateTestPhoto(int id = 1, bool isMain = false, bool isApproved = false, int appUserId = 2)
{
return new Photo
{
Id = id,
AppUserId = appUserId,
IsApproved = isApproved,
IsMain = isMain,
Url = $"https://test.com/photo{id}.jpg"
};
}

private static AppUser CreateTestUser(int appUserId, params Photo[] photos)
{
return new AppUser
{
Id = appUserId,
UserName = "testuser",
KnownAs = "Test User",
Gender = "male",
City = "TestCity",
Country = "TestCountry",
Photos = photos.ToList()
};
}

private void SetupMocksForSuccessfulApproval(Photo photo, AppUser user)
{
_photoRepoMock.Setup(r => r.GetPhotoById(photo.Id)).ReturnsAsync(photo);
_photoRepoMock.Setup(r => r.GetUserByPhotoId(photo.Id)).ReturnsAsync(user); // OVO JE KLJUČNO
_unitOfWorkMock.Setup(u => u.Complete()).ReturnsAsync(true);
}

#endregion
}
}
Loading
Loading