Skip to content

Commit

Permalink
Merge branch 'main' into add-event
Browse files Browse the repository at this point in the history
  • Loading branch information
adrien-vieilleribiere committed Apr 4, 2024
2 parents 43030f0 + 27f5b65 commit 00d056c
Show file tree
Hide file tree
Showing 5 changed files with 4,078 additions and 5,874 deletions.
45 changes: 45 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: CI

# Controls when the workflow will run
on: push

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./backend

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 20.0.0

- name: Setup yarn
run: npm install -g yarn

- name: Install Dependencies
run: yarn install

# Compile
- name: Compile
run: yarn hardhat compile

# Solhint
- name: Linter
run: yarn hardhat check

# Tests
- name: Test
run: REPORT_GAS=true yarn hardhat test

# Coverage
- name: Coverage
id: coverage
run: yarn hardhat coverage
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[![CI](https://github.com/volkanguneri/Allfeat-Hackaton-Musicmint/actions/workflows/ci.yml/badge.svg)](https://github.com/volkanguneri/Allfeat-Hackaton-Musicmint/actions/workflows/ci.yml)

## Dependencies

### Backend
Expand All @@ -6,6 +8,11 @@ To install the backend dependencies, navigate to the backend directory and run:

`yarn install`

To see available options, run:
```
sh a_launcher.sh
```

### Frontend

To install the frontend dependencies, navigate to the frontend directory and run:
Expand Down
41 changes: 34 additions & 7 deletions backend/contracts/Albums.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
contract Albums is Ownable {

struct Album {
uint16 id;
uint64 maxSupply;
uint64 price;
string uri;
uint16 currentSongId;
}

struct Song {
Expand All @@ -20,7 +22,7 @@ contract Albums is Ownable {


mapping(uint16 albumId => Album) private _albums;
mapping(uint16 albumId => Song[] songs) private _albumSongs;
mapping(uint16 albumId => mapping(uint16 songID => Song)) private _albumSongs;

// The current album ID. Used to generate next one.
uint16 private _currentAlbumId;
Expand All @@ -47,12 +49,18 @@ contract Albums is Ownable {
uint64 price,
string memory album_uri
) external onlyOwner returns (uint256) {
require(maxSupply > 0, "Max supply must be greater than 0");
require(price > 0, "Price must be greater than 0");
require(bytes(album_uri).length > 0, "URI must not be empty");

uint16 albumId = _currentAlbumId;

_albums[albumId] = Album({
id: albumId,
maxSupply: maxSupply,
price: price,
uri: album_uri
uri: album_uri,
currentSongId: 0
});

emit ItemCreated(msg.sender, albumId, album_uri, 0, maxSupply);
Expand All @@ -70,27 +78,46 @@ contract Albums is Ownable {
) external onlyOwner returns (uint256) {
require(maxSupply > 0, "Max supply must be greater than 0");
require(price > 0, "Price must be greater than 0");
require(bytes(songUri).length > 0, "URI must not be empty");

Album storage album = _albums[albumId];
require(album.maxSupply > 0, "Album does not exist");

uint256 numberOfSongs = _albumSongs[albumId].length;
uint256 numberOfSongs = _albums[albumId].currentSongId;
require(numberOfSongs < maxSupply, "Maximum supply reached");
require(numberOfSongs <= type(uint16).max, "Value exceeds uint16 range");

uint16 songId = uint16(numberOfSongs) + 1;

_albumSongs[albumId].push(Song({
uint16 songId = album.currentSongId;


_albumSongs[albumId][songId] = Song({
id: songId,
maxSupply: maxSupply,
price: price,
uri: songUri
});

}));
album.currentSongId++;

emit ItemCreated(msg.sender, albumId, songUri, songId, maxSupply);

return songId;
}

function getAlbum(uint16 albumId) external view returns (Album memory) {
return _albums[albumId];
}

function getAlbumsCount() external view returns (uint256) {
return _currentAlbumId;
}

function getSong(uint16 albumId, uint16 songId) external view returns (Song memory) {
return _albumSongs[albumId][songId];
}

function getAlbumSongsCount(uint16 albumId) external view returns (uint256) {
return _albums[albumId].currentSongId;
}

}
127 changes: 127 additions & 0 deletions backend/test/Albums.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
const {
loadFixture,
} = require("@nomicfoundation/hardhat-toolbox/network-helpers");
const { ethers } = require("hardhat");
const { utils } = require("ethers");
const { expect } = require("chai");

describe("Albums test", async function () {
const deployAlbumsFixture = async () => {
const [owner, otherAccount1] = await ethers.getSigners();

const Albums = await ethers.getContractFactory("Albums");
const albums = await Albums.deploy(owner.address);

return { albums, owner, otherAccount1 };
};

it("should deploy with no errors", async function () {
const { albums, owner } = await loadFixture(deployAlbumsFixture);
expect(albums.address).is.not.null;
expect(await albums.owner()).to.equal(owner.address);
});

describe("createAlbum", async function () {
let albums, owner, otherAccount1;
beforeEach(async () => {
const result = await loadFixture(deployAlbumsFixture);
albums = result.albums;
owner = result.owner;
otherAccount1 = result.otherAccount1;
});

it("should fail if it is not created by the owner", async function () {
await expect(
albums.connect(otherAccount1).createAlbum(100, 100, "uri album")
).to.be.revertedWithCustomError(albums, "OwnableUnauthorizedAccount");
});

it("should fail if price is 0", async function () {
await expect(
albums.connect(owner).createAlbum(1000, 0, "uri album")
).to.be.revertedWith("Price must be greater than 0");
});

it("should fail if maxSupply is 0", async function () {
await expect(
albums.connect(owner).createAlbum(0, 100, "uri album")
).to.be.revertedWith("Max supply must be greater than 0");
});

it("should fail if uri is empty", async function () {
await expect(
albums.connect(owner).createAlbum(1000, 100, "")
).to.be.revertedWith("URI must not be empty");
});

context("when the album is created", async function () {
beforeEach(async function () {
expect(await albums.getAlbumsCount()).to.equal(0);
await albums.connect(owner).createAlbum(1000n, 100n, "uri album");
});

it("should create an album", async function () {
expect(await albums.getAlbumsCount()).to.equal(1);
const album = await albums.getAlbum(0);

expect(album.id).to.equal(0);
expect(album.price).to.equal(100);
expect(album.maxSupply).to.equal(1000);
expect(album.uri).to.equal("uri album");
});

it("should emit an ItemCreated event", async () => {
expect(await albums.connect(owner).createAlbum(1000, 100, "uri"))
.to.emit(albums, "ItemCreated")
.withArgs(owner.address, 0, 1000, 100, "uri album");
});

describe("createSong", async function () {
it("should fail if it is not created by the owner", async function () {
await expect(
albums.connect(otherAccount1).createSong(0, 1000, 100, "uri song")
).to.be.revertedWithCustomError(albums, "OwnableUnauthorizedAccount");
});

it("should fail if albumId does not exist", async function () {
await expect(
albums.connect(owner).createSong(5, 1000, 100, "uri song")
).to.be.revertedWith("Album does not exist");
});

it("should fail if price is 0", async function () {
await expect(
albums.connect(owner).createSong(0, 1000, 0, "uri song")
).to.be.revertedWith("Price must be greater than 0");
});

it("should fail if uri is empty", async function () {
await expect(
albums.connect(owner).createSong(0, 1000, 100, "")
).to.be.revertedWith("URI must not be empty");
});

context("when the song is created", async function () {
it("should create a song", async function () {
expect(await albums.getAlbumSongsCount(0)).to.equal(0);
await albums.connect(owner).createSong(0, 1000, 100, "uri song");

expect(await albums.getAlbumSongsCount(0)).to.equal(1);
const song = await albums.getSong(0, 0);

expect(song.id).to.equal(0);
expect(song.price).to.equal(100);
expect(song.maxSupply).to.equal(1000);
expect(song.uri).to.equal("uri song");
});

it("should emit an ItemCreated event", async () => {
expect(await albums.connect(owner).createSong(0, 1000, 100, "uri"))
.to.emit(albums, "ItemCreated")
.withArgs(owner.address, 0, 1000, 100, "uri song");
});
});
});
});
});
});
Loading

0 comments on commit 00d056c

Please sign in to comment.