Files
schedule-planner/frontend/cypress/e2e/api-crud.cy.js
Hera Zhao d3f3b4f37b
Some checks failed
Test / build-check (push) Successful in 3s
PR Preview / test (pull_request) Successful in 3s
PR Preview / teardown-preview (pull_request) Has been skipped
Test / e2e-test (push) Failing after 55s
PR Preview / deploy-preview (pull_request) Failing after 40s
Refactor to Vue 3 + FastAPI + SQLite architecture
- Backend: FastAPI + SQLite (WAL mode), 22 tables, ~40 API endpoints
- Frontend: Vue 3 + Vite + Pinia + Vue Router, 8 views, 3 stores
- Database: migrate from JSON file to SQLite with proper schema
- Dockerfile: multi-stage build (node + python)
- Deploy: K8s manifests (namespace, deployment, service, ingress, pvc, backup)
- CI/CD: Gitea Actions (test, deploy, PR preview at pr-$id.planner.oci.euphon.net)
- Tests: 20 Cypress E2E test files, 196 test cases, ~85% coverage
- Doc: test-coverage.md with full feature coverage report

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 21:18:22 +00:00

232 lines
7.8 KiB
JavaScript

describe('API CRUD Operations', () => {
const uid = () => 'cy_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6)
// ---- Notes ----
it('POST /api/notes creates a note', () => {
const id = uid()
cy.request('POST', '/api/notes', { id, text: 'E2E test note', tag: '灵感' }).then(res => {
expect(res.status).to.eq(200)
expect(res.body.ok).to.be.true
})
})
it('DELETE /api/notes/:id deletes a note', () => {
const id = uid()
cy.request('POST', '/api/notes', { id, text: 'to delete', tag: '灵感' })
cy.request('DELETE', `/api/notes/${id}`).then(res => {
expect(res.status).to.eq(200)
})
})
// ---- Todos ----
it('POST /api/todos creates a todo', () => {
const id = uid()
cy.request('POST', '/api/todos', { id, text: 'E2E todo', quadrant: 'q1', done: 0 }).then(res => {
expect(res.body.ok).to.be.true
})
})
it('POST /api/todos updates a todo (upsert)', () => {
const id = uid()
cy.request('POST', '/api/todos', { id, text: 'before', quadrant: 'q1', done: 0 })
cy.request('POST', '/api/todos', { id, text: 'after', quadrant: 'q2', done: 1 }).then(res => {
expect(res.body.ok).to.be.true
})
cy.request('/api/todos').then(res => {
const todo = res.body.find(t => t.id === id)
expect(todo.text).to.eq('after')
expect(todo.quadrant).to.eq('q2')
expect(todo.done).to.eq(1)
})
})
it('DELETE /api/todos/:id deletes a todo', () => {
const id = uid()
cy.request('POST', '/api/todos', { id, text: 'to delete', quadrant: 'q1', done: 0 })
cy.request('DELETE', `/api/todos/${id}`).then(res => {
expect(res.status).to.eq(200)
})
})
// ---- Inbox ----
it('POST /api/inbox creates inbox item', () => {
const id = uid()
cy.request('POST', '/api/inbox', { id, text: 'inbox test' }).then(res => {
expect(res.body.ok).to.be.true
})
})
it('DELETE /api/inbox clears all inbox', () => {
cy.request('DELETE', '/api/inbox').then(res => {
expect(res.body.ok).to.be.true
})
cy.request('/api/inbox').then(res => {
expect(res.body).to.have.length(0)
})
})
// ---- Reminders ----
it('POST /api/reminders creates a reminder', () => {
const id = uid()
cy.request('POST', '/api/reminders', { id, text: 'test reminder', time: '09:00', repeat: 'daily', enabled: 1 }).then(res => {
expect(res.body.ok).to.be.true
})
})
it('DELETE /api/reminders/:id deletes a reminder', () => {
const id = uid()
cy.request('POST', '/api/reminders', { id, text: 'to delete', repeat: 'none', enabled: 1 })
cy.request('DELETE', `/api/reminders/${id}`).then(res => {
expect(res.status).to.eq(200)
})
})
// ---- Goals ----
it('POST /api/goals creates and updates a goal', () => {
const id = uid()
cy.request('POST', '/api/goals', { id, name: 'test goal', month: '2026-06', checks: '{}' }).then(res => {
expect(res.body.ok).to.be.true
})
cy.request('POST', '/api/goals', { id, name: 'updated goal', month: '2026-07', checks: '{"2026-07-01":true}' })
cy.request('/api/goals').then(res => {
const goal = res.body.find(g => g.id === id)
expect(goal.name).to.eq('updated goal')
})
})
// ---- Checklists ----
it('POST /api/checklists creates a checklist', () => {
const id = uid()
cy.request('POST', '/api/checklists', { id, title: 'test list', items: '[]', archived: 0 }).then(res => {
expect(res.body.ok).to.be.true
})
})
// ---- Sleep ----
it('POST /api/sleep creates a record', () => {
cy.request('POST', '/api/sleep', { date: '2026-01-01', time: '22:30' }).then(res => {
expect(res.body.ok).to.be.true
})
})
it('POST /api/sleep upserts on same date', () => {
cy.request('POST', '/api/sleep', { date: '2026-01-02', time: '22:00' })
cy.request('POST', '/api/sleep', { date: '2026-01-02', time: '23:00' })
cy.request('/api/sleep').then(res => {
const rec = res.body.find(r => r.date === '2026-01-02')
expect(rec.time).to.eq('23:00')
})
})
it('DELETE /api/sleep/:date deletes a record', () => {
cy.request('POST', '/api/sleep', { date: '2026-01-03', time: '21:00' })
cy.request('DELETE', '/api/sleep/2026-01-03').then(res => {
expect(res.status).to.eq(200)
})
})
// ---- Gym ----
it('POST /api/gym creates a record', () => {
const id = uid()
cy.request('POST', '/api/gym', { id, date: '2026-04-07', type: '跑步', duration: '30min', note: '5km' }).then(res => {
expect(res.body.ok).to.be.true
})
})
// ---- Period ----
it('POST /api/period creates a record', () => {
const id = uid()
cy.request('POST', '/api/period', { id, start_date: '2026-04-01', end_date: '2026-04-05', note: '' }).then(res => {
expect(res.body.ok).to.be.true
})
})
// ---- Docs ----
it('POST /api/docs creates a doc', () => {
const id = uid()
cy.request('POST', '/api/docs', { id, name: 'test doc', icon: '📖', keywords: 'test', extract_rule: 'none' }).then(res => {
expect(res.body.ok).to.be.true
})
})
it('POST /api/doc-entries creates an entry', () => {
const docId = uid()
const entryId = uid()
cy.request('POST', '/api/docs', { id: docId, name: 'doc for entry', icon: '📄', keywords: '', extract_rule: 'none' })
cy.request('POST', '/api/doc-entries', { id: entryId, doc_id: docId, text: 'entry text' }).then(res => {
expect(res.body.ok).to.be.true
})
cy.request('/api/docs').then(res => {
const doc = res.body.find(d => d.id === docId)
expect(doc.entries).to.have.length(1)
expect(doc.entries[0].text).to.eq('entry text')
})
})
// ---- Bugs ----
it('POST /api/bugs creates a bug', () => {
const id = uid()
cy.request('POST', '/api/bugs', { id, text: 'test bug', status: 'open' }).then(res => {
expect(res.body.ok).to.be.true
})
})
it('DELETE /api/bugs/:id deletes a bug', () => {
const id = uid()
cy.request('POST', '/api/bugs', { id, text: 'to delete', status: 'open' })
cy.request('DELETE', `/api/bugs/${id}`).then(res => {
expect(res.status).to.eq(200)
})
})
// ---- Schedule ----
it('POST /api/schedule-modules creates a module', () => {
const id = uid()
cy.request('POST', '/api/schedule-modules', { id, name: 'work', emoji: '💼', color: '#667eea', sort_order: 0 }).then(res => {
expect(res.body.ok).to.be.true
})
})
it('POST /api/schedule-slots creates a slot', () => {
const modId = uid()
cy.request('POST', '/api/schedule-modules', { id: modId, name: 'slot test', emoji: '📌', color: '#333', sort_order: 0 })
cy.request('POST', '/api/schedule-slots', { date: '2026-04-07', time_slot: '09:00', module_id: modId }).then(res => {
expect(res.body.ok).to.be.true
})
})
// ---- Reviews ----
it('POST /api/reviews creates a review', () => {
cy.request('POST', '/api/reviews', { week: '2026-W15', data: '{"wins":"test"}' }).then(res => {
expect(res.body.ok).to.be.true
})
})
// ---- Health check-in ----
it('POST /api/health-items creates an item', () => {
const id = uid()
cy.request('POST', '/api/health-items', { id, name: 'vitamin C', type: 'health' }).then(res => {
expect(res.body.ok).to.be.true
})
})
it('POST /api/health-plans saves a plan', () => {
cy.request('POST', '/api/health-plans', { month: '2026-04', type: 'health', item_ids: '["item1"]' }).then(res => {
expect(res.body.ok).to.be.true
})
})
it('POST /api/health-checks toggles a check', () => {
cy.request('POST', '/api/health-checks', { date: '2026-04-07', type: 'health', item_id: 'item1', checked: 1 }).then(res => {
expect(res.body.ok).to.be.true
})
})
// ---- Backup ----
it('POST /api/backup triggers backup', () => {
cy.request('POST', '/api/backup').then(res => {
expect(res.body.ok).to.be.true
})
})
})