XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEServerManager

【Java】我的世界XFE服务器管理器

公开
关注 0 Fork 0 Star 0
UTF-8
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, expect, it, vi } from 'vitest';
import { App } from '../app';
import { ServerProvider } from '../context/server-context';
import { ApiClient } from '../lib/api';
import { I18nProvider } from '../lib/i18n';

function preparedClient(): ApiClient {
  const client = new ApiClient();
  vi.spyOn(client, 'session').mockResolvedValue({
    authenticated: true,
    csrfToken: 'csrf',
    actor: { id: 'owner-id', displayName: 'owner', roles: ['owner'], permissions: [], totpVerified: true },
  });
  vi.spyOn(client, 'status').mockResolvedValue({ sampledAt: '2026-01-01T00:00:00Z', uptimeSeconds: 1, playersOnline: 1 });
  vi.spyOn(client, 'features').mockResolvedValue({ consoleEnabled: false, moderationEnabled: false, claimsEnabled: false, worldTrackingEnabled: false, rollbackEnabled: false });
  vi.spyOn(client, 'subscribe').mockReturnValue(() => undefined);
  vi.spyOn(client, 'players').mockResolvedValue({ items: [{ uuid: '11111111-1111-1111-1111-111111111111', name: 'Alex', online: true }] });
  return client;
}

describe('player inventory management', () => {
  it('previews and submits one slot with the snapshot revision', async () => {
    localStorage.setItem('xfesm.locale', 'en-US');
    location.hash = '/players';
    const client = preparedClient();
    vi.spyOn(client, 'playerInventory').mockResolvedValue({
      playerUuid: '11111111-1111-1111-1111-111111111111',
      container: 'inventory',
      revision: 12,
      online: true,
      slots: [{ slot: 0, itemId: 'minecraft:stone', count: 2, structuredData: {}, opaqueDataPresent: false, opaqueDataFingerprint: '' }],
    });
    const update = vi.spyOn(client, 'updatePlayerInventory').mockResolvedValue({
      id: 'inventory-op', kind: 'inventory.update', state: 'SUCCEEDED', revision: 13, diffs: [],
    });
    const user = userEvent.setup();

    render(<I18nProvider><ServerProvider client={client}><App /></ServerProvider></I18nProvider>);
    await user.click(await screen.findByRole('button', { name: 'Manage' }));
    await user.click(screen.getByRole('tab', { name: 'Inventory' }));
    await user.click(await screen.findByRole('button', { name: 'Slot 0: minecraft:stone × 2' }));
    const item = screen.getByLabelText('Item registry ID');
    await user.clear(item);
    await user.type(item, 'minecraft:diamond');
    const count = screen.getByLabelText('Count (0–127)');
    await user.clear(count);
    await user.type(count, '4');
    await user.type(screen.getByLabelText(/Audit reason/), 'Replace reward item');

    await user.click(screen.getByRole('button', { name: 'Preview slot change' }));
    expect(update).not.toHaveBeenCalled();
    expect(screen.getByText(/minecraft:stone × 2/)).toBeInTheDocument();
    await user.click(screen.getByRole('button', { name: 'Confirm CAS update' }));

    await waitFor(() => expect(update).toHaveBeenCalledOnce());
    expect(update).toHaveBeenCalledWith('11111111-1111-1111-1111-111111111111', 'inventory', 12, {
      slot: 0,
      itemId: 'minecraft:diamond',
      count: 4,
      structuredData: {},
      opaqueDataPresent: false,
      opaqueDataFingerprint: '',
    }, 'Replace reward item');
  });

  it('blocks edits to slots whose opaque data cannot be preserved', async () => {
    localStorage.setItem('xfesm.locale', 'en-US');
    location.hash = '/players';
    const client = preparedClient();
    vi.spyOn(client, 'playerInventory').mockResolvedValue({
      playerUuid: '11111111-1111-1111-1111-111111111111', container: 'inventory', revision: 2, online: true,
      slots: [{ slot: 5, itemId: 'minecraft:book', count: 1, structuredData: {}, opaqueDataPresent: true, opaqueDataFingerprint: 'abc' }],
    });
    const update = vi.spyOn(client, 'updatePlayerInventory');
    const user = userEvent.setup();

    render(<I18nProvider><ServerProvider client={client}><App /></ServerProvider></I18nProvider>);
    await user.click(await screen.findByRole('button', { name: 'Manage' }));
    await user.click(screen.getByRole('tab', { name: 'Inventory' }));
    await user.click(await screen.findByRole('button', { name: 'Slot 5: minecraft:book × 1' }));

    expect(screen.getByText(/editing is blocked to prevent data loss/)).toBeInTheDocument();
    expect(screen.getByRole('button', { name: 'Preview slot change' })).toBeDisabled();
    expect(update).not.toHaveBeenCalled();
  });
});