← back to Wine Finder Next

__tests__/unit/membershipDatabase.test.ts

473 lines

// Unit Tests for Membership Database Operations
import {
  bottleDb,
  membershipUnitDb,
  governanceVoteDb,
  marketplaceDb,
  sampleUnitDb,
  getConfig,
} from '@/lib/membershipDatabase';
import {
  createMockBottle,
  createMockMembershipUnit,
  createMockGovernanceVote,
  createMockMarketplaceListing,
} from '../fixtures/testData';

describe('Membership Database', () => {
  describe('bottleDb', () => {
    it('should create a new bottle', () => {
      const bottleData = {
        name: 'Test Wine',
        vintage: 2010,
        producer: 'Test Producer',
        region: 'Test Region',
        size: '750ml',
        totalMembershipUnits: 1000,
        availableMembershipUnits: 1000,
        estimatedValue: 50000,
        provenance: 'Test provenance',
        status: 'active' as const,
      };

      const bottle = bottleDb.create(bottleData);

      expect(bottle).toHaveProperty('id');
      expect(bottle.id).toMatch(/^bottle_\d+_[a-z0-9]{9}$/);
      expect(bottle.name).toBe(bottleData.name);
      expect(bottle.vintage).toBe(bottleData.vintage);
      expect(bottle).toHaveProperty('createdAt');
      expect(bottle).toHaveProperty('updatedAt');
    });

    it('should get all bottles', () => {
      const initialCount = bottleDb.getAll().length;

      bottleDb.create({
        name: 'Test Wine 1',
        vintage: 2010,
        producer: 'Test Producer',
        region: 'Test Region',
        size: '750ml',
        totalMembershipUnits: 1000,
        availableMembershipUnits: 1000,
        estimatedValue: 50000,
        provenance: 'Test provenance',
        status: 'active' as const,
      });

      const bottles = bottleDb.getAll();
      expect(bottles.length).toBeGreaterThan(initialCount);
    });

    it('should get bottle by ID', () => {
      const bottle = bottleDb.create({
        name: 'Test Wine Get',
        vintage: 2010,
        producer: 'Test Producer',
        region: 'Test Region',
        size: '750ml',
        totalMembershipUnits: 1000,
        availableMembershipUnits: 1000,
        estimatedValue: 50000,
        provenance: 'Test provenance',
        status: 'active' as const,
      });

      const retrieved = bottleDb.getById(bottle.id);
      expect(retrieved).toBeDefined();
      expect(retrieved?.id).toBe(bottle.id);
      expect(retrieved?.name).toBe('Test Wine Get');
    });

    it('should update a bottle', () => {
      const bottle = bottleDb.create({
        name: 'Test Wine Update',
        vintage: 2010,
        producer: 'Test Producer',
        region: 'Test Region',
        size: '750ml',
        totalMembershipUnits: 1000,
        availableMembershipUnits: 1000,
        estimatedValue: 50000,
        provenance: 'Test provenance',
        status: 'active' as const,
      });

      const updated = bottleDb.update(bottle.id, {
        status: 'unlocked',
        availableMembershipUnits: 500,
      });

      expect(updated).toBeDefined();
      expect(updated?.status).toBe('unlocked');
      expect(updated?.availableMembershipUnits).toBe(500);
    });

    it('should return null when updating non-existent bottle', () => {
      const updated = bottleDb.update('bottle_0_nonexist', { status: 'locked' });
      expect(updated).toBeNull();
    });
  });

  describe('membershipUnitDb', () => {
    it('should create a membership unit', () => {
      const unitData = {
        bottleId: 'bottle_1700000000000_test12345',
        tokenId: 'TEST_TOKEN_1',
        ownerId: 'user_1',
        ownerEmail: 'test@example.com',
        ownerName: 'Test User',
        purchasePrice: 10.50,
        purchaseDate: new Date(),
        status: 'active' as const,
        votingPower: 1,
        transactionHistory: [],
      };

      const unit = membershipUnitDb.create(unitData);

      expect(unit).toHaveProperty('id');
      expect(unit.id).toMatch(/^unit_\d+_[a-z0-9]{9}$/);
      expect(unit.ownerId).toBe('user_1');
      expect(unit.purchasePrice).toBe(10.50);
    });

    it('should get units by bottle ID', () => {
      const bottleId = 'bottle_1700000000000_testbottle';

      membershipUnitDb.create({
        bottleId,
        tokenId: 'TOKEN_1',
        ownerId: 'user_1',
        ownerEmail: 'test1@example.com',
        ownerName: 'User 1',
        purchasePrice: 10,
        purchaseDate: new Date(),
        status: 'active',
        votingPower: 1,
        transactionHistory: [],
      });

      membershipUnitDb.create({
        bottleId,
        tokenId: 'TOKEN_2',
        ownerId: 'user_2',
        ownerEmail: 'test2@example.com',
        ownerName: 'User 2',
        purchasePrice: 10,
        purchaseDate: new Date(),
        status: 'active',
        votingPower: 1,
        transactionHistory: [],
      });

      const units = membershipUnitDb.getByBottleId(bottleId);
      expect(units.length).toBeGreaterThanOrEqual(2);
      expect(units.every(u => u.bottleId === bottleId)).toBe(true);
    });

    it('should get units by owner ID', () => {
      const ownerId = 'user_testowner';

      membershipUnitDb.create({
        bottleId: 'bottle_1',
        tokenId: 'TOKEN_OWNER_1',
        ownerId,
        ownerEmail: 'owner@example.com',
        ownerName: 'Owner',
        purchasePrice: 10,
        purchaseDate: new Date(),
        status: 'active',
        votingPower: 1,
        transactionHistory: [],
      });

      const units = membershipUnitDb.getByOwnerId(ownerId);
      expect(units.length).toBeGreaterThanOrEqual(1);
      expect(units.every(u => u.ownerId === ownerId)).toBe(true);
    });

    it('should transfer a membership unit', () => {
      const unit = membershipUnitDb.create({
        bottleId: 'bottle_1',
        tokenId: 'TOKEN_TRANSFER',
        ownerId: 'user_1',
        ownerEmail: 'user1@example.com',
        ownerName: 'User 1',
        purchasePrice: 10,
        purchaseDate: new Date(),
        status: 'active',
        votingPower: 1,
        transactionHistory: [],
      });

      const transferred = membershipUnitDb.transfer(
        unit.id,
        'user_2',
        'user2@example.com',
        'User 2',
        12.50
      );

      expect(transferred).toBeDefined();
      expect(transferred?.ownerId).toBe('user_2');
      expect(transferred?.ownerEmail).toBe('user2@example.com');
      expect(transferred?.transactionHistory.length).toBe(1);
      expect(transferred?.transactionHistory[0].price).toBe(12.50);
    });
  });

  describe('governanceVoteDb', () => {
    it('should create a governance vote', () => {
      const voteData = {
        bottleId: 'bottle_1700000000000_test12345',
        proposal: 'Test proposal',
        proposalType: 'unlock' as const,
        description: 'Test description',
        startDate: new Date(),
        endDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
        status: 'active' as const,
        quorumRequired: 50,
        approvalThreshold: 66,
        createdBy: 'admin',
      };

      const vote = governanceVoteDb.create(voteData);

      expect(vote).toHaveProperty('id');
      expect(vote.id).toMatch(/^vote_\d+_[a-z0-9]{9}$/);
      expect(vote.votesFor).toBe(0);
      expect(vote.votesAgainst).toBe(0);
      expect(vote.votesAbstain).toBe(0);
      expect(vote.votes).toEqual([]);
    });

    it('should get active votes', () => {
      governanceVoteDb.create({
        bottleId: 'bottle_1',
        proposal: 'Active proposal',
        proposalType: 'unlock',
        description: 'Test',
        startDate: new Date(Date.now() - 1000),
        endDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
        status: 'active',
        quorumRequired: 50,
        approvalThreshold: 66,
        createdBy: 'admin',
      });

      const activeVotes = governanceVoteDb.getActive();
      expect(activeVotes.length).toBeGreaterThanOrEqual(1);
      expect(activeVotes.every(v => v.status === 'active')).toBe(true);
    });

    it('should cast a vote', () => {
      const bottle = bottleDb.create({
        name: 'Test Vote Bottle',
        vintage: 2010,
        producer: 'Test',
        region: 'Test',
        size: '750ml',
        totalMembershipUnits: 100,
        availableMembershipUnits: 100,
        estimatedValue: 10000,
        provenance: 'Test',
        status: 'active',
      });

      const unit = membershipUnitDb.create({
        bottleId: bottle.id,
        tokenId: 'TOKEN_VOTE_TEST',
        ownerId: 'user_voter',
        ownerEmail: 'voter@example.com',
        ownerName: 'Voter',
        purchasePrice: 100,
        purchaseDate: new Date(),
        status: 'active',
        votingPower: 1,
        transactionHistory: [],
      });

      const vote = governanceVoteDb.create({
        bottleId: bottle.id,
        proposal: 'Test vote casting',
        proposalType: 'unlock',
        description: 'Test',
        startDate: new Date(),
        endDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
        status: 'active',
        quorumRequired: 50,
        approvalThreshold: 66,
        createdBy: 'admin',
      });

      const result = governanceVoteDb.castVote(vote.id, 'user_voter', unit.id, 'for');

      expect(result).toBeDefined();
      expect(result?.votesFor).toBe(1);
      expect(result?.votes.length).toBe(1);
      expect(result?.votes[0].choice).toBe('for');
    });

    it('should prevent double voting', () => {
      const bottle = bottleDb.create({
        name: 'Test Double Vote Bottle',
        vintage: 2010,
        producer: 'Test',
        region: 'Test',
        size: '750ml',
        totalMembershipUnits: 100,
        availableMembershipUnits: 100,
        estimatedValue: 10000,
        provenance: 'Test',
        status: 'active',
      });

      const unit = membershipUnitDb.create({
        bottleId: bottle.id,
        tokenId: 'TOKEN_DOUBLE_VOTE',
        ownerId: 'user_double',
        ownerEmail: 'double@example.com',
        ownerName: 'Double',
        purchasePrice: 100,
        purchaseDate: new Date(),
        status: 'active',
        votingPower: 1,
        transactionHistory: [],
      });

      const vote = governanceVoteDb.create({
        bottleId: bottle.id,
        proposal: 'Test double voting',
        proposalType: 'unlock',
        description: 'Test',
        startDate: new Date(),
        endDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
        status: 'active',
        quorumRequired: 50,
        approvalThreshold: 66,
        createdBy: 'admin',
      });

      // First vote should succeed
      const firstVote = governanceVoteDb.castVote(vote.id, 'user_double', unit.id, 'for');
      expect(firstVote).toBeDefined();

      // Second vote should fail
      const secondVote = governanceVoteDb.castVote(vote.id, 'user_double', unit.id, 'against');
      expect(secondVote).toBeNull();
    });
  });

  describe('marketplaceDb', () => {
    it('should create a marketplace listing', () => {
      const listingData = {
        listingType: 'membership' as const,
        itemId: 'unit_1700000000000_test',
        bottleId: 'bottle_1700000000000_test',
        sellerId: 'user_1',
        sellerName: 'Test Seller',
        price: 15.50,
        currency: 'USD' as const,
        status: 'active' as const,
      };

      const listing = marketplaceDb.create(listingData);

      expect(listing).toHaveProperty('id');
      expect(listing.id).toMatch(/^listing_\d+_[a-z0-9]{9}$/);
      expect(listing.price).toBe(15.50);
      expect(listing).toHaveProperty('listedAt');
    });

    it('should get active listings', () => {
      marketplaceDb.create({
        listingType: 'membership',
        itemId: 'unit_active',
        bottleId: 'bottle_1',
        sellerId: 'user_1',
        sellerName: 'Seller',
        price: 10,
        currency: 'USD',
        status: 'active',
      });

      const activeListings = marketplaceDb.getActive();
      expect(activeListings.length).toBeGreaterThanOrEqual(1);
      expect(activeListings.every(l => l.status === 'active')).toBe(true);
    });

    it('should complete a sale', () => {
      const unit = membershipUnitDb.create({
        bottleId: 'bottle_sale',
        tokenId: 'TOKEN_SALE',
        ownerId: 'user_seller',
        ownerEmail: 'seller@example.com',
        ownerName: 'Seller',
        purchasePrice: 10,
        purchaseDate: new Date(),
        status: 'active',
        votingPower: 1,
        transactionHistory: [],
      });

      const listing = marketplaceDb.create({
        listingType: 'membership',
        itemId: unit.id,
        bottleId: 'bottle_sale',
        sellerId: 'user_seller',
        sellerName: 'Seller',
        price: 12,
        currency: 'USD',
        status: 'active',
      });

      const completed = marketplaceDb.completeSale(
        listing.id,
        'user_buyer',
        'buyer@example.com',
        'Buyer'
      );

      expect(completed).toBeDefined();
      expect(completed?.status).toBe('sold');
      expect(completed?.soldTo).toBe('user_buyer');
      expect(completed).toHaveProperty('soldAt');

      // Verify ownership transfer
      const transferredUnit = membershipUnitDb.getById(unit.id);
      expect(transferredUnit?.ownerId).toBe('user_buyer');
    });

    it('should cancel a listing', () => {
      const listing = marketplaceDb.create({
        listingType: 'membership',
        itemId: 'unit_cancel',
        bottleId: 'bottle_1',
        sellerId: 'user_1',
        sellerName: 'Seller',
        price: 10,
        currency: 'USD',
        status: 'active',
      });

      const cancelled = marketplaceDb.cancel(listing.id);

      expect(cancelled).toBeDefined();
      expect(cancelled?.status).toBe('cancelled');
    });
  });

  describe('Platform Configuration', () => {
    it('should get platform config', () => {
      const config = getConfig();

      expect(config).toHaveProperty('votingQuorumPercentage');
      expect(config).toHaveProperty('votingApprovalThreshold');
      expect(config).toHaveProperty('platformFeePercentage');
      expect(typeof config.votingQuorumPercentage).toBe('number');
    });
  });
});