Reputation: 1155
I'm using Solidity/Truffle. I have two contracts called Category and Post. Post is inherited from Category (Post is Category
).
Contract Category has an array called categories
. I have a function in contract Category called isCategoryExists
.
When I try to call this function from contract Category, everything is ok. But when I want to call this function from contract Post, I receive false
because categories.length
is 0
.
Category.sol:
ragma solidity >=0.4.22 <0.9.0;
import "./ICategory.sol";
/// @title Create, edit and manage categories
contract Category is ICategory {
// State variables
uint256 currentIndex;
CategoryStruct[] private categories;
mapping(address => CategoryStruct[]) categoriesByUser;
// Modifiers
modifier onlyValidInput(CategoryInputStruct memory _input) {
bytes memory errCode;
bytes memory title = bytes(_input.title);
if (title.length == 0) {
errCode = bytes("invalidTitle");
}
if (errCode.length > 0) {
revert(string(errCode));
}
_;
}
modifier onlyValidIndex(uint256 index) {
if (isCategoryExists(index)) {
_;
} else {
revert("Invalid index.");
}
}
// Constructor
constructor() {
currentIndex = categories.length;
}
// Functions
/// @notice Check if category exists
function isCategoryExists(uint256 index) public view returns (bool) {
if (index >= categories.length) {
return false;
}
return categories[index].isExist;
}
/// @notice This function creates a category.
/// @dev Before this function, the entered data is validated with onlyValidInput modifier.
function createCategory(
CategoryInputStruct memory _input,
LocationStruct memory _location
) external onlyValidInput(_input) returns (bool) {
CategoryStruct memory newCategory = CategoryStruct({
id: currentIndex,
user: msg.sender,
title: _input.title,
isExist: true
});
categories.push(newCategory);
categoriesByUser[msg.sender].push(newCategory);
emit CategoryCreated(currentIndex);
currentIndex++;
return true;
}
}
Post.sol:
pragma solidity >=0.4.22 <0.9.0;
import "../category/Category.sol";
/// @title Create, edit and manage posts
contract Post is Category {
// State variables
uint256 currentPostIndex;
struct PostStruct {
uint256 id;
address user;
string title;
string body;
uint256 categoryId;
}
struct PostInputStruct {
string title;
string body;
uint256 categoryId;
}
PostStruct[] private posts;
mapping(address => uint256[]) postIndexesByUser; // example: 0x01234 => [3, 5, 24, 112, 448]
// Modifiers
modifier onlyValidPostInput(PostInputStruct memory _input) {
bytes memory errCode;
bytes memory title = bytes(_input.title);
bytes memory body = bytes(_input.body);
uint256 categoryId = uint256(_input.categoryId);
if (title.length == 0) {
errCode = bytes("invalidTitle");
} else if (body.length == 0) {
errCode = bytes("invalidBody");
}
if (errCode.length > 0) {
revert(string(errCode));
}
_;
}
// Constructor
constructor() {
currentPostIndex = posts.length;
}
// Functions
/// @notice This function creates a post.
/// @dev Before this function, the entered data is validated with onlyValidPostInput modifier.
function createPost(PostInputStruct memory _input)
external
onlyValidPostInput(_input)
returns (bool)
{
bool isExist = isCategoryExists(_input.categoryId);
PostStruct memory newPost = PostStruct({
id: currentPostIndex,
user: msg.sender,
title: _input.title,
body: _input.body,
categoryId: _input.categoryId
});
posts.push(newPost);
postIndexesByUser[msg.sender].push(currentPostIndex);
currentPostIndex++;
return true;
}
}
I make this call from the JS test environment in Truffle.
What I guess: I think everything resets every time I run the tests. I'm not sure yet. This is a bit strange and has wasted a few days of my time.
Category.test.js:
const Chance = require("chance");
const Category = artifacts.require("Category");
const chance = new Chance();
contract("Category", (accounts) => {
// Setup 1 account.
const accountOne = accounts[0];
const accountTwo = accounts[1];
let categoryInstance;
before(async () => {
categoryInstance = await Category.deployed();
});
it("should create a category", async () => {
const title = chance.sentence();
// Create category
const categoryCreated = await categoryInstance.createCategory([title], {
from: accountOne,
});
// Get a category by array index
const category = await categoryInstance.getCategoryByIndex(0);
assert.equal(category.id, 0, "There is no data for this index");
assert.equal(category.title, title, "title is not equal");
});
it("Should return true because a category exists", async () => {
const isExists = await categoryInstance.isCategoryExists(0, {
from: accountOne,
});
assert.equal(isExists, true, "Category exists but result is false.");
});
});
Post.test.js
const Chance = require("chance");
const Category = artifacts.require("Category");
const Post = artifacts.require("post");
const chance = new Chance();
contract("Post", (accounts) => {
// Setup 1 account.
const accountOne = accounts[0];
const accountTwo = accounts[1];
let categoryInstance;
let postInstance;
before(async () => {
// Create a sample category
categoryInstance = await Category.deployed();
const title = chance.sentence();
// Create category
const categoryCreated = await categoryInstance.createCategory(
[title, purpose, area],
[polygon],
{
from: accountOne,
}
);
postInstance = await Post.deployed();
});
it("should create a post", async () => {
// Generate sample data
const title = chance.sentence();
const body = chance.paragraph();
const thumbnail = chance.url();
const categoryId = 0;
// Create post
const postCreated = await postInstance.createPost(
[title, body, thumbnail, categoryId],
{
from: accountOne,
}
);
// Get a post by array index
const post = await postInstance.getPostByIndex(0);
assert.equal(post.id, 0, "There is no data for this index");
assert.equal(post.title, title, "title is not equal");
assert.equal(post.categoryId, categoryId, "categoryId is not equal");
});
});
Upvotes: 1
Views: 633
Reputation: 4153
The problem is that you have misunderstood the concept of inheritance. The fact that Post
inherits from Category
does not mean that instances of Post
are sharing the state with instances of Category
. What it means is that Post
is also a Category
, so an object/instance of Post has that same state (variables) and behavior (functions/methods) of a Category
contained within itself. In your case, this actually means an object of Post
has its own array categories
and that one is being checked when you call createPost
, and it will of course be empty as you have never added a category using that object. The only way that array can be non-empty is if you call createCategory
from postInstance
, not from categoryInstance
.
P.S. Just to quickly clarify what I meant by "nothing should be persisted". The test suite should be created in a way that each test is independent, no test should ever depend on the execution of some other test. One should be able to run any test from the suite on its own and have it passing. I initially thought this was the problem, as you did not share the entire code.
Upvotes: 1