255 lines
7.9 KiB
JavaScript
255 lines
7.9 KiB
JavaScript
/**
|
|
* Property-based tests for chat state management
|
|
* **Feature: miniapp-frontend, Property 22: Unread Badge Display**
|
|
* **Validates: Requirements 13.5**
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach } from 'vitest'
|
|
import * as fc from 'fast-check'
|
|
import { setActivePinia, createPinia } from 'pinia'
|
|
|
|
// Import mock before other modules
|
|
import '../mocks/uni.js'
|
|
import { useChatStore, MessageType, MessageStatus } from '../../store/chat.js'
|
|
|
|
describe('Chat Store Property Tests', () => {
|
|
beforeEach(() => {
|
|
// Create a fresh pinia instance for each test
|
|
setActivePinia(createPinia())
|
|
})
|
|
|
|
/**
|
|
* Property 22: Unread Badge Display
|
|
* *For any* unread message count > 0, the message tab SHALL display a red badge.
|
|
* **Validates: Requirements 13.5**
|
|
*/
|
|
it('Property 22: Unread Badge Display - badge should show when unread count > 0', () => {
|
|
fc.assert(
|
|
fc.property(
|
|
// Generate positive unread counts
|
|
fc.integer({ min: 1, max: 1000 }),
|
|
(unreadCount) => {
|
|
const chatStore = useChatStore()
|
|
|
|
// Use the pure function to test
|
|
const shouldShow = chatStore.shouldShowUnreadBadge(unreadCount)
|
|
|
|
// Badge should show for any positive count
|
|
return shouldShow === true
|
|
}
|
|
),
|
|
{ numRuns: 100 }
|
|
)
|
|
})
|
|
|
|
/**
|
|
* Property 22 (continued): Badge should NOT show when unread count is 0
|
|
*/
|
|
it('Property 22: Unread Badge Display - badge should not show when unread count is 0', () => {
|
|
const chatStore = useChatStore()
|
|
|
|
// Use the pure function to test
|
|
const shouldShow = chatStore.shouldShowUnreadBadge(0)
|
|
|
|
// Badge should not show for zero count
|
|
expect(shouldShow).toBe(false)
|
|
})
|
|
|
|
/**
|
|
* Property 22 (continued): Badge should NOT show for negative counts
|
|
*/
|
|
it('Property 22: Unread Badge Display - badge should not show for negative counts', () => {
|
|
fc.assert(
|
|
fc.property(
|
|
// Generate negative counts (edge case)
|
|
fc.integer({ min: -1000, max: -1 }),
|
|
(negativeCount) => {
|
|
const chatStore = useChatStore()
|
|
|
|
// Use the pure function to test
|
|
const shouldShow = chatStore.shouldShowUnreadBadge(negativeCount)
|
|
|
|
// Badge should not show for negative counts
|
|
return shouldShow === false
|
|
}
|
|
),
|
|
{ numRuns: 100 }
|
|
)
|
|
})
|
|
|
|
/**
|
|
* Property: Total unread count should equal sum of session unread counts
|
|
*/
|
|
it('Total unread count should equal sum of session unread counts', () => {
|
|
fc.assert(
|
|
fc.property(
|
|
// Generate array of sessions with unread counts
|
|
fc.array(
|
|
fc.record({
|
|
sessionId: fc.integer({ min: 1 }),
|
|
targetUserId: fc.integer({ min: 1 }),
|
|
targetNickname: fc.string({ minLength: 1 }),
|
|
targetAvatar: fc.string(),
|
|
lastMessage: fc.string(),
|
|
lastMessageTime: fc.string(),
|
|
unreadCount: fc.integer({ min: 0, max: 100 })
|
|
}),
|
|
{ minLength: 0, maxLength: 10 }
|
|
),
|
|
(sessions) => {
|
|
const chatStore = useChatStore()
|
|
|
|
// Set sessions
|
|
chatStore.setSessions(sessions)
|
|
|
|
// Calculate expected total
|
|
const expectedTotal = sessions.reduce(
|
|
(sum, s) => sum + (s.unreadCount || 0),
|
|
0
|
|
)
|
|
|
|
// Total should match
|
|
return chatStore.totalUnreadCount === expectedTotal
|
|
}
|
|
),
|
|
{ numRuns: 100 }
|
|
)
|
|
})
|
|
|
|
/**
|
|
* Property: hasUnreadMessages getter should match totalUnreadCount > 0
|
|
*/
|
|
it('hasUnreadMessages getter should match totalUnreadCount > 0', () => {
|
|
fc.assert(
|
|
fc.property(
|
|
fc.array(
|
|
fc.record({
|
|
sessionId: fc.integer({ min: 1 }),
|
|
targetUserId: fc.integer({ min: 1 }),
|
|
targetNickname: fc.string({ minLength: 1 }),
|
|
targetAvatar: fc.string(),
|
|
lastMessage: fc.string(),
|
|
lastMessageTime: fc.string(),
|
|
unreadCount: fc.integer({ min: 0, max: 100 })
|
|
}),
|
|
{ minLength: 0, maxLength: 10 }
|
|
),
|
|
(sessions) => {
|
|
const chatStore = useChatStore()
|
|
|
|
// Set sessions
|
|
chatStore.setSessions(sessions)
|
|
|
|
// hasUnreadMessages should match whether total > 0
|
|
const expectedHasUnread = chatStore.totalUnreadCount > 0
|
|
|
|
return chatStore.hasUnreadMessages === expectedHasUnread
|
|
}
|
|
),
|
|
{ numRuns: 100 }
|
|
)
|
|
})
|
|
|
|
/**
|
|
* Property: Marking session as read should decrease total unread count
|
|
*/
|
|
it('Marking session as read should decrease total unread count', () => {
|
|
fc.assert(
|
|
fc.property(
|
|
// Generate sessions with at least one having unread messages
|
|
fc.array(
|
|
fc.record({
|
|
sessionId: fc.integer({ min: 1, max: 100 }),
|
|
targetUserId: fc.integer({ min: 1 }),
|
|
targetNickname: fc.string({ minLength: 1 }),
|
|
targetAvatar: fc.string(),
|
|
lastMessage: fc.string(),
|
|
lastMessageTime: fc.string(),
|
|
unreadCount: fc.integer({ min: 0, max: 50 })
|
|
}),
|
|
{ minLength: 1, maxLength: 5 }
|
|
).filter(sessions => sessions.some(s => s.unreadCount > 0)),
|
|
(sessions) => {
|
|
const chatStore = useChatStore()
|
|
|
|
// Ensure unique session IDs
|
|
const uniqueSessions = sessions.map((s, i) => ({
|
|
...s,
|
|
sessionId: i + 1
|
|
}))
|
|
|
|
// Set sessions
|
|
chatStore.setSessions(uniqueSessions)
|
|
|
|
// Find a session with unread messages
|
|
const sessionWithUnread = uniqueSessions.find(s => s.unreadCount > 0)
|
|
if (!sessionWithUnread) return true // Skip if no unread
|
|
|
|
const totalBefore = chatStore.totalUnreadCount
|
|
const sessionUnread = sessionWithUnread.unreadCount
|
|
|
|
// Mark as read
|
|
chatStore.markSessionAsRead(sessionWithUnread.sessionId)
|
|
|
|
const totalAfter = chatStore.totalUnreadCount
|
|
|
|
// Total should decrease by the session's unread count
|
|
return totalAfter === totalBefore - sessionUnread
|
|
}
|
|
),
|
|
{ numRuns: 100 }
|
|
)
|
|
})
|
|
|
|
/**
|
|
* Property: Receiving message in non-current session should increment unread
|
|
*/
|
|
it('Receiving message in non-current session should increment unread count', () => {
|
|
fc.assert(
|
|
fc.property(
|
|
fc.integer({ min: 1, max: 100 }), // sessionId
|
|
fc.integer({ min: 101, max: 200 }), // different currentSessionId
|
|
fc.string({ minLength: 1 }), // message content
|
|
(sessionId, currentSessionId, content) => {
|
|
const chatStore = useChatStore()
|
|
|
|
// Set up session
|
|
chatStore.setSessions([{
|
|
sessionId,
|
|
targetUserId: 1,
|
|
targetNickname: 'Test',
|
|
targetAvatar: '',
|
|
lastMessage: '',
|
|
lastMessageTime: '',
|
|
unreadCount: 0
|
|
}])
|
|
|
|
// Set current session to a different one
|
|
chatStore.setCurrentSession(currentSessionId)
|
|
|
|
const unreadBefore = chatStore.totalUnreadCount
|
|
|
|
// Receive message in the non-current session
|
|
chatStore.receiveMessage({
|
|
id: 1,
|
|
sessionId,
|
|
senderId: 2,
|
|
receiverId: 1,
|
|
messageType: MessageType.TEXT,
|
|
content,
|
|
status: MessageStatus.DELIVERED,
|
|
createTime: new Date().toISOString(),
|
|
isMine: false
|
|
})
|
|
|
|
const unreadAfter = chatStore.totalUnreadCount
|
|
|
|
// Unread count should increase by 1
|
|
return unreadAfter === unreadBefore + 1
|
|
}
|
|
),
|
|
{ numRuns: 100 }
|
|
)
|
|
})
|
|
})
|