Skip to content

1003 feature request player data should hold the date when the player joined the clan - #1011

Merged
tickBit merged 9 commits into
devfrom
1003-feature-request-player-data-should-hold-the-date-when-the-player-joined-the-clan
Sep 29, 2026
Merged

tickBit merged 9 commits into
devfrom
1003-feature-request-player-data-should-hold-the-date-when-the-player-joined-the-clan

Conversation

@constf03

Copy link
Copy Markdown
Contributor

Brief description

Added new field clan_joindate in Player schema. The field stores the date when a Player has joined a Clan. If the Player isn't in a Clan the field is null. This field's value is handled automatically in Clan join/leave methods.

Tested in-memory utilizing MongoDB memory replica set:

// script
import mongoose from 'mongoose';
import { MongoMemoryReplSet } from 'mongodb-memory-server';
import MQTTConnector from './src/common/service/notificator/MQTTConnector';
import { JoinService } from './src/clan/join/join.service';
import { PlayerSchema } from './src/player/schemas/player.schema';
import { MemberClanRole } from './src/clan/role/initializationClanRoles';
import { ModelName } from './src/common/enum/modelName.enum';
import { ObjectId } from 'mongodb';

(MQTTConnector as any).getInstance = () => ({ publish: () => {} });

(async () => {
  console.log('--- clan_joindate Validation (in-memory MongoDB) ---');
  const replSet = await MongoMemoryReplSet.create({ replSet: { count: 1 } });
  const conn = await mongoose
    .createConnection(replSet.getUri(), {
      dbName: 'clan_joindate_check',
    })
    .asPromise();

  let failed = false;
  const check = (label: string, ok: boolean, result: any) => {
    if (!ok) failed = true;
    console.log(label, ok ? 'PASS' : `FAIL (${result})`);
  };

  try {
    const playerModel = conn.model(ModelName.PLAYER, PlayerSchema);
    const clan = {
      _id: new ObjectId().toString(),
      playerCount: 5,
      isOpen: true,
      roles: [{ _id: new ObjectId().toString(), name: MemberClanRole.name }],
    };
    const ok = [true, null];
    const clanService = {
      readOneById: async () => [clan, null],
      deleteOneById: async () => ok,
      basicService: { updateOne: async () => ok },
    };
    const roomService = {
      deactivateRoom: async () => ok,
      basicService: { findOneAndUpdate: async () => [{ _id: 'room' }, null] },
    };
    const soulHomeService = {
      basicService: { readOne: async () => [{ _id: 'soulHome' }, null] },
    };
    const counterFactory = {
      create: () => ({ decreaseByIdOnOne: async () => true }),
    };
    const joinService = new JoinService(
      counterFactory as any,
      clanService as any,
      roomService as any,
      soulHomeService as any,
      playerModel as any,
      conn,
    );

    const created = await playerModel.create({
      name: 'joinDateTester',
      uniqueIdentifier: 'join-date-tester',
      backpackCapacity: 10,
    });
    const playerId = created._id.toString();
    check(
      '1. New player has no clan_joindate (null expected):',
      created.clan_joindate === null,
      created.clan_joindate,
    );

    const before = Date.now();
    await joinService.handleJoinRequest(clan._id, playerId);
    const after = Date.now();
    const joined = await playerModel.findById(playerId);
    const joinTime = joined.clan_joindate?.getTime();
    check(
      '2. clan_id is set after join:',
      joined.clan_id?.toString() === clan._id,
      joined.clan_id,
    );
    check(
      '3. clan_joindate is a Date after join:',
      joined.clan_joindate instanceof Date,
      joined.clan_joindate,
    );
    check(
      '4. clan_joindate is the join time:',
      joinTime >= before && joinTime <= after,
      joined.clan_joindate?.toISOString(),
    );
    console.log(
      '   clan_joindate stored:',
      joined.clan_joindate?.toISOString(),
    );

    await joinService.leaveClan(playerId);
    const left = await playerModel.findById(playerId);
    check('5. clan_id null after leave:', left.clan_id === null, left.clan_id);
    check(
      '6. clan_joindate null after leave:',
      left.clan_joindate === null,
      left.clan_joindate,
    );

    await joinService.handleJoinRequest(clan._id, playerId);
    await joinService.removePlayerFromClan(playerId, clan._id);
    const removed = await playerModel.findById(playerId);
    check(
      '7. clan_joindate null after being removed from clan:',
      removed.clan_joindate === null,
      removed.clan_joindate,
    );
  } catch (error) {
    failed = true;
    console.error('FAIL (unexpected error):', error);
  } finally {
    await conn.close();
    await replSet.stop();
    process.exit(failed ? 1 : 0);
  }
})();

Log:

PS C:\ProgrammingStuff\Altzone-Server> npx ts-node ./test.ts
[dotenv@17.2.3] injecting env (28) from .env -- tip: ⚙️  suppress all logs with { quiet: true }
--- clan_joindate Validation (in-memory MongoDB) ---
1. New player has no clan_joindate (null expected): PASS
2. clan_id is set after join: PASS
3. clan_joindate is a Date after join: PASS
4. clan_joindate is the join time: PASS
   clan_joindate stored: 2026-09-28T13:18:39.872Z
5. clan_id null after leave: PASS
6. clan_joindate null after leave: PASS
7. clan_joindate null after being removed from clan: PASS
PS C:\ProgrammingStuff\Altzone-Server> 

Change list

  • Added new field clan_joindate?: Date in Player schema & exposed it in PlayerDTO
  • Updated relevant methods in JoinService and ClanService to handle the clan_joindate field
  • Updated JoinService and ClanService tests to cover the clan_joindate handling part also included it in TesterAccountService and PlayerBuilders
  • Wrote migration file 20260927120000-add-player-clan-joindate.js. Existing Players in Clans get default date 01-09-2026 as required in the issue , those without a Clan get null.

@codecov-alt

codecov-alt Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
src/box/accountClaimer/testerAccount.service.ts 95.77% <ø> (ø)
src/clan/clan.service.ts 86.63% <ø> (ø)
src/clan/join/join.service.ts 86.47% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@tickBit

tickBit commented Sep 28, 2026

Copy link
Copy Markdown
Collaborator

I updated the branch from UI. It looks like the test coverage is too low because 2 lines of code in player.service.ts are not covered with the tests.

@constf03

Copy link
Copy Markdown
Contributor Author

I updated the branch from UI. It looks like the test coverage is too low because 2 lines of code in player.service.ts are not covered with the tests.

Yes it's the lines I added in updateOnePostHook but I noticed it doesn't have a test suite at all, it's probably a unused method from the old PUT /player flow, it doesn't seem to be called anywhere in player.controller or the service itself. I could verify from AI and if it's unused I should probably revert the commit

@constf03

Copy link
Copy Markdown
Contributor Author

I'm not sure if it's a good idea to always add more and more code especially for parts that are not tested to begin with and are likely unused/old leftovers. The updateOnePostHook method doesn't have JSdoc either so I rather revert the commit that changes updateOnePostHook and go with that.

@tickBit

tickBit commented Sep 29, 2026

Copy link
Copy Markdown
Collaborator

I'm not sure if it's a good idea to always add more and more code especially for parts that are not tested to begin with and are likely unused/old leftovers. The updateOnePostHook method doesn't have JSdoc either so I rather revert the commit that changes updateOnePostHook and go with that.

I asked AI about updateOnePostHook method. It's used in some decorators, but it is mostly unused and according to AI, updateOnePostHook method doesn't work as intended.

Here's summary:

updateOnePostHook is only invoked by the deprecated AddBasicService decorator. Updates performed through the newer BasicService implementation bypass the hook entirely, making its behavior inconsistent and potentially leaving clan player counts out of sync.

@constf03

Copy link
Copy Markdown
Contributor Author

Yea that's what I also understood and also Player clan join/leave should be fully handled by JoinService and ClanService that are covered in this PR so the PlayerService updateOnePostHook is probably not relevant for the clan_joindate.....

@tickBit tickBit left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job 👍 ! Approved.

I'll do the required DB migration soon.

@tickBit
tickBit merged commit 92e0cf3 into dev Sep 29, 2026
5 checks passed
@tickBit
tickBit deleted the 1003-feature-request-player-data-should-hold-the-date-when-the-player-joined-the-clan branch September 29, 2026 09:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Feature request: Player Data should hold the date when the player joined the clan.

2 participants