next.js frontend w/ mongodb local backend
This commit is contained in:
parent
e24e17e42b
commit
f333d9ba00
18 changed files with 3144 additions and 337 deletions
18
.gitignore
vendored
18
.gitignore
vendored
|
|
@ -131,5 +131,23 @@ dmypy.json
|
||||||
# OSX
|
# OSX
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
# Node.js / Next.js
|
||||||
|
node_modules/
|
||||||
|
.next/
|
||||||
|
.swc/
|
||||||
|
out/
|
||||||
|
build/
|
||||||
|
.vercel/
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Node.js Debug Log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Local env files
|
||||||
|
.env*.local
|
||||||
|
|
||||||
# Etc
|
# Etc
|
||||||
.idea
|
.idea
|
||||||
157
DEVELOPMENT.md
Normal file
157
DEVELOPMENT.md
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
# 开发指南
|
||||||
|
|
||||||
|
## 环境要求
|
||||||
|
|
||||||
|
- Python 3.x
|
||||||
|
- Node.js (推荐 v16 或更高版本)
|
||||||
|
- MongoDB
|
||||||
|
|
||||||
|
## 数据库配置
|
||||||
|
|
||||||
|
1. 安装 MongoDB
|
||||||
|
- Windows: 参考 https://www.runoob.com/mongodb/mongodb-window-install.html
|
||||||
|
- macOS: 使用 Homebrew 安装
|
||||||
|
```bash
|
||||||
|
brew tap mongodb/brew
|
||||||
|
brew install mongodb-community
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 启动 MongoDB 服务
|
||||||
|
```bash
|
||||||
|
# Windows
|
||||||
|
mongod --dbpath <你的数据库路径>
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
brew services start mongodb-community
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 验证 MongoDB 是否正常运行
|
||||||
|
```bash
|
||||||
|
mongosh
|
||||||
|
use flask_db
|
||||||
|
show dbs
|
||||||
|
```
|
||||||
|
|
||||||
|
## 后端服务启动
|
||||||
|
|
||||||
|
1. 安装 Python 依赖
|
||||||
|
```bash
|
||||||
|
# 如果使用 poetry
|
||||||
|
poetry install
|
||||||
|
|
||||||
|
# 如果使用 pip
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 启动 Flask 服务器
|
||||||
|
```bash
|
||||||
|
# 使用 poetry
|
||||||
|
poetry run python -m flask --app src.backend.app run --debug
|
||||||
|
|
||||||
|
# 直接使用 python
|
||||||
|
python -m flask --app src.backend.app run --debug
|
||||||
|
|
||||||
|
# 如果localhost:5000相应api无法访问,但是127.0.0.1:5000可以访问,考虑使用以下命令启动
|
||||||
|
poetry run python -m flask --app src.backend.app run --host=0.0.0.0 --debug
|
||||||
|
# 在macos系统下,可能要解决端口冲突
|
||||||
|
Port 5000 is in use by another program. Either identify and stop that program, or start the server with a different port.
|
||||||
|
On macOS, try disabling the 'AirPlay Receiver' service from System Preferences -> General -> AirDrop & Handoff.
|
||||||
|
```
|
||||||
|
|
||||||
|
服务器将在 http://localhost:5000 上运行
|
||||||
|
|
||||||
|
## 前端服务启动
|
||||||
|
|
||||||
|
1. 进入前端目录
|
||||||
|
```bash
|
||||||
|
cd src/frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 安装依赖
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 启动开发服务器
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
前端服务将在 http://localhost:3000 上运行
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### MongoDB 连接问题
|
||||||
|
|
||||||
|
如果遇到 MongoDB 连接错误:
|
||||||
|
1. 确保 MongoDB 服务正在运行
|
||||||
|
2. 检查 MongoDB 默认端口 (27017) 是否被占用
|
||||||
|
3. 确认数据库路径是否正确
|
||||||
|
|
||||||
|
### 后端服务启动失败
|
||||||
|
|
||||||
|
1. 确保所有依赖都已正确安装
|
||||||
|
2. 检查 MongoDB 连接是否正常
|
||||||
|
3. 确保端口 5000 未被占用
|
||||||
|
|
||||||
|
### 前端开发服务器问题
|
||||||
|
|
||||||
|
1. 确保 Node.js 版本兼容
|
||||||
|
2. 删除 node_modules 目录并重新安装依赖
|
||||||
|
3. 确保端口 3000 未被占用
|
||||||
|
|
||||||
|
### 运行测试
|
||||||
|
|
||||||
|
1. 安装测试依赖
|
||||||
|
```bash
|
||||||
|
# 使用 poetry 添加测试依赖
|
||||||
|
poetry add pytest --dev
|
||||||
|
|
||||||
|
# 如果使用 pip
|
||||||
|
pip install pytest
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 运行测试
|
||||||
|
使用 Poetry 运行测试:
|
||||||
|
```bash
|
||||||
|
# 运行所有测试
|
||||||
|
poetry run pytest
|
||||||
|
|
||||||
|
# 运行单个测试文件
|
||||||
|
poetry run pytest tests/test_app.py
|
||||||
|
|
||||||
|
# 查看详细的测试输出
|
||||||
|
poetry run pytest -v
|
||||||
|
|
||||||
|
# 显示测试失败的详细信息
|
||||||
|
poetry run pytest -vv
|
||||||
|
```
|
||||||
|
|
||||||
|
如果测试失败,使用 `-vv` 参数可以查看更详细的错误信息和断言失败的具体原因。
|
||||||
|
```
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
前端服务将在 http://localhost:3000 上运行
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### MongoDB 连接问题
|
||||||
|
|
||||||
|
如果遇到 MongoDB 连接错误:
|
||||||
|
1. 确保 MongoDB 服务正在运行
|
||||||
|
2. 检查 MongoDB 默认端口 (27017) 是否被占用
|
||||||
|
3. 确认数据库路径是否正确
|
||||||
|
|
||||||
|
### 后端服务启动失败
|
||||||
|
|
||||||
|
1. 确保所有依赖都已正确安装
|
||||||
|
2. 检查 MongoDB 连接是否正常
|
||||||
|
3. 确保端口 5000 未被占用
|
||||||
|
|
||||||
|
### 前端开发服务器问题
|
||||||
|
|
||||||
|
1. 确保 Node.js 版本兼容
|
||||||
|
2. 删除 node_modules 目录并重新安装依赖
|
||||||
|
3. 确保端口 3000 未被占用
|
||||||
17
db_scripts/backup_mongo.py
Normal file
17
db_scripts/backup_mongo.py
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# Define the database name and backup directory
|
||||||
|
db_name = "flask_db"
|
||||||
|
|
||||||
|
# Example: export BACKUP_DIR="/Users/<myname>/Codes/pyxr_db_backup"
|
||||||
|
backup_dir = os.getenv('BACKUP_DIR', '/default/backup/path') # Use environment variable
|
||||||
|
|
||||||
|
# Create a timestamped backup file name
|
||||||
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
backup_file = os.path.join(backup_dir, f"{db_name}_backup_{timestamp}.gz")
|
||||||
|
|
||||||
|
# Run the mongodump command to back up the database
|
||||||
|
os.system(f"mongodump --db {db_name} --archive={backup_file} --gzip")
|
||||||
|
|
||||||
|
print(f"Backup completed: {backup_file}")
|
||||||
91
poetry.lock
generated
91
poetry.lock
generated
|
|
@ -1,4 +1,4 @@
|
||||||
# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blinker"
|
name = "blinker"
|
||||||
|
|
@ -6,7 +6,6 @@ version = "1.9.0"
|
||||||
description = "Fast, simple object-to-object and broadcast signaling"
|
description = "Fast, simple object-to-object and broadcast signaling"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
groups = ["main"]
|
|
||||||
files = [
|
files = [
|
||||||
{file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"},
|
{file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"},
|
||||||
{file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"},
|
{file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"},
|
||||||
|
|
@ -18,7 +17,6 @@ version = "8.1.8"
|
||||||
description = "Composable command line interface toolkit"
|
description = "Composable command line interface toolkit"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
groups = ["main"]
|
|
||||||
files = [
|
files = [
|
||||||
{file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"},
|
{file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"},
|
||||||
{file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"},
|
{file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"},
|
||||||
|
|
@ -33,8 +31,6 @@ version = "0.4.6"
|
||||||
description = "Cross-platform colored terminal text."
|
description = "Cross-platform colored terminal text."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||||
groups = ["main"]
|
|
||||||
markers = "platform_system == \"Windows\""
|
|
||||||
files = [
|
files = [
|
||||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||||
|
|
@ -46,7 +42,6 @@ version = "2.7.0"
|
||||||
description = "DNS toolkit"
|
description = "DNS toolkit"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
groups = ["main"]
|
|
||||||
files = [
|
files = [
|
||||||
{file = "dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86"},
|
{file = "dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86"},
|
||||||
{file = "dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1"},
|
{file = "dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1"},
|
||||||
|
|
@ -67,7 +62,6 @@ version = "3.1.0"
|
||||||
description = "A simple framework for building complex web applications."
|
description = "A simple framework for building complex web applications."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
groups = ["main"]
|
|
||||||
files = [
|
files = [
|
||||||
{file = "flask-3.1.0-py3-none-any.whl", hash = "sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136"},
|
{file = "flask-3.1.0-py3-none-any.whl", hash = "sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136"},
|
||||||
{file = "flask-3.1.0.tar.gz", hash = "sha256:5f873c5184c897c8d9d1b05df1e3d01b14910ce69607a117bd3277098a5836ac"},
|
{file = "flask-3.1.0.tar.gz", hash = "sha256:5f873c5184c897c8d9d1b05df1e3d01b14910ce69607a117bd3277098a5836ac"},
|
||||||
|
|
@ -84,13 +78,37 @@ Werkzeug = ">=3.1"
|
||||||
async = ["asgiref (>=3.2)"]
|
async = ["asgiref (>=3.2)"]
|
||||||
dotenv = ["python-dotenv"]
|
dotenv = ["python-dotenv"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flask-cors"
|
||||||
|
version = "4.0.2"
|
||||||
|
description = "A Flask extension adding a decorator for CORS support"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "Flask_Cors-4.0.2-py2.py3-none-any.whl", hash = "sha256:38364faf1a7a5d0a55bd1d2e2f83ee9e359039182f5e6a029557e1f56d92c09a"},
|
||||||
|
{file = "flask_cors-4.0.2.tar.gz", hash = "sha256:493b98e2d1e2f1a4720a7af25693ef2fe32fbafec09a2f72c59f3e475eda61d2"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
Flask = ">=0.9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iniconfig"
|
||||||
|
version = "2.0.0"
|
||||||
|
description = "brain-dead simple config-ini parsing"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
|
||||||
|
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itsdangerous"
|
name = "itsdangerous"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
description = "Safely pass data to untrusted environments and back."
|
description = "Safely pass data to untrusted environments and back."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
groups = ["main"]
|
|
||||||
files = [
|
files = [
|
||||||
{file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"},
|
{file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"},
|
||||||
{file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"},
|
{file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"},
|
||||||
|
|
@ -102,7 +120,6 @@ version = "3.1.5"
|
||||||
description = "A very fast and expressive template engine."
|
description = "A very fast and expressive template engine."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
groups = ["main"]
|
|
||||||
files = [
|
files = [
|
||||||
{file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"},
|
{file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"},
|
||||||
{file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"},
|
{file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"},
|
||||||
|
|
@ -120,7 +137,6 @@ version = "3.0.2"
|
||||||
description = "Safely add untrusted strings to HTML/XML markup."
|
description = "Safely add untrusted strings to HTML/XML markup."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
groups = ["main"]
|
|
||||||
files = [
|
files = [
|
||||||
{file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"},
|
{file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"},
|
||||||
{file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"},
|
{file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"},
|
||||||
|
|
@ -185,13 +201,38 @@ files = [
|
||||||
{file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"},
|
{file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "packaging"
|
||||||
|
version = "24.2"
|
||||||
|
description = "Core utilities for Python packages"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
|
||||||
|
{file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pluggy"
|
||||||
|
version = "1.5.0"
|
||||||
|
description = "plugin and hook calling mechanisms for python"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
|
||||||
|
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
dev = ["pre-commit", "tox"]
|
||||||
|
testing = ["pytest", "pytest-benchmark"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pymongo"
|
name = "pymongo"
|
||||||
version = "4.11"
|
version = "4.11"
|
||||||
description = "Python driver for MongoDB <http://www.mongodb.org>"
|
description = "Python driver for MongoDB <http://www.mongodb.org>"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
groups = ["main"]
|
|
||||||
files = [
|
files = [
|
||||||
{file = "pymongo-4.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1971039a8e3aab139e0382b26a9670cd34f43c5301da267360b9a640b637d09b"},
|
{file = "pymongo-4.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1971039a8e3aab139e0382b26a9670cd34f43c5301da267360b9a640b637d09b"},
|
||||||
{file = "pymongo-4.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b4878e92ae255a05756399a4e2b428f0fd3529561eacd9f4781a70ad5311397e"},
|
{file = "pymongo-4.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b4878e92ae255a05756399a4e2b428f0fd3529561eacd9f4781a70ad5311397e"},
|
||||||
|
|
@ -253,13 +294,32 @@ snappy = ["python-snappy"]
|
||||||
test = ["pytest (>=8.2)", "pytest-asyncio (>=0.24.0)"]
|
test = ["pytest (>=8.2)", "pytest-asyncio (>=0.24.0)"]
|
||||||
zstd = ["zstandard"]
|
zstd = ["zstandard"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytest"
|
||||||
|
version = "8.3.4"
|
||||||
|
description = "pytest: simple powerful testing with Python"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"},
|
||||||
|
{file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||||
|
iniconfig = "*"
|
||||||
|
packaging = "*"
|
||||||
|
pluggy = ">=1.5,<2"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-dotenv"
|
name = "python-dotenv"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
description = "Read key-value pairs from a .env file and set them as environment variables"
|
description = "Read key-value pairs from a .env file and set them as environment variables"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
groups = ["main"]
|
|
||||||
files = [
|
files = [
|
||||||
{file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"},
|
{file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"},
|
||||||
{file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"},
|
{file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"},
|
||||||
|
|
@ -274,7 +334,6 @@ version = "3.1.3"
|
||||||
description = "The comprehensive WSGI web application library."
|
description = "The comprehensive WSGI web application library."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
groups = ["main"]
|
|
||||||
files = [
|
files = [
|
||||||
{file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"},
|
{file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"},
|
||||||
{file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"},
|
{file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"},
|
||||||
|
|
@ -287,6 +346,6 @@ MarkupSafe = ">=2.1.1"
|
||||||
watchdog = ["watchdog (>=2.3)"]
|
watchdog = ["watchdog (>=2.3)"]
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.1"
|
lock-version = "2.0"
|
||||||
python-versions = ">=3.14"
|
python-versions = ">=3.11,<4.0"
|
||||||
content-hash = "92cefaa6220f3c4295418cd77943e3358937e37d8da7c2082d272a44801dad72"
|
content-hash = "fb9237517aad08372c5658ca9803833ed0ddbc66f5b0778de7d03e315d6c3c35"
|
||||||
|
|
|
||||||
2
poetry.toml
Normal file
2
poetry.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
[virtualenvs]
|
||||||
|
in-project = true
|
||||||
|
|
@ -1,18 +1,20 @@
|
||||||
[project]
|
[tool.poetry]
|
||||||
name = "pyxr"
|
name = "pyxr"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = ""
|
description = ""
|
||||||
authors = [
|
authors = ["seemygesture <seemygesture@gmail.com>"]
|
||||||
{name = "seemygesture",email = "seemygesture@gmail.com"}
|
|
||||||
]
|
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.14"
|
packages = [{include = "src"}]
|
||||||
dependencies = [
|
|
||||||
"flask (>=3.1.0,<4.0.0)",
|
|
||||||
"pymongo (>=4.11,<5.0)",
|
|
||||||
"python-dotenv (>=1.0.1,<2.0.0)"
|
|
||||||
]
|
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = ">=3.11,<4.0"
|
||||||
|
flask = ">=3.1.0,<4.0.0"
|
||||||
|
pymongo = ">=4.11,<5.0"
|
||||||
|
python-dotenv = ">=1.0.1,<2.0.0"
|
||||||
|
flask-cors = ">=4.0.0,<5.0.0"
|
||||||
|
|
||||||
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
pytest = "^8.3.4"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
||||||
|
|
|
||||||
26
pyxr/app.py
26
pyxr/app.py
|
|
@ -1,26 +0,0 @@
|
||||||
from flask import Flask, render_template, request, url_for, redirect
|
|
||||||
from pymongo import MongoClient
|
|
||||||
from bson.objectid import ObjectId
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
|
|
||||||
client = MongoClient('localhost', 27017)
|
|
||||||
|
|
||||||
db = client.flask_db
|
|
||||||
todos = db.todos
|
|
||||||
|
|
||||||
@app.route('/', methods=('GET', 'POST'))
|
|
||||||
def index():
|
|
||||||
if request.method=='POST':
|
|
||||||
content = request.form['content']
|
|
||||||
degree = request.form['degree']
|
|
||||||
todos.insert_one({'content': content, 'degree': degree})
|
|
||||||
return redirect(url_for('index'))
|
|
||||||
|
|
||||||
all_todos = todos.find()
|
|
||||||
return render_template('index.html', todos=all_todos)
|
|
||||||
|
|
||||||
@app.post('/<id>/delete/')
|
|
||||||
def delete(id):
|
|
||||||
todos.delete_one({"_id": ObjectId(id)})
|
|
||||||
return redirect(url_for('index'))
|
|
||||||
59
src/backend/app.py
Normal file
59
src/backend/app.py
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
from flask import Flask, request, jsonify
|
||||||
|
from flask_cors import CORS, cross_origin
|
||||||
|
from pymongo import MongoClient
|
||||||
|
from bson.objectid import ObjectId
|
||||||
|
from bson.json_util import dumps
|
||||||
|
from datetime import datetime
|
||||||
|
from bson import errors as bson_errors
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
cors = CORS(app, resources={
|
||||||
|
r"/api/*": {
|
||||||
|
"origins": ["http://localhost:3000", "http://localhost:3001"],
|
||||||
|
"methods": ["GET", "POST", "DELETE", "OPTIONS"],
|
||||||
|
"allow_headers": ["Content-Type"]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.config['CORS_HEADERS'] = 'Content-Type'
|
||||||
|
|
||||||
|
client = MongoClient('localhost', 27017)
|
||||||
|
|
||||||
|
db = client.flask_db
|
||||||
|
todos = db.todos
|
||||||
|
|
||||||
|
# 确保UUID字段的唯一性
|
||||||
|
todos.create_index('uuid', unique=True)
|
||||||
|
|
||||||
|
@app.route('/api/todos', methods=['GET'])
|
||||||
|
@cross_origin()
|
||||||
|
def get_todos():
|
||||||
|
all_todos = todos.find()
|
||||||
|
return dumps(list(all_todos))
|
||||||
|
|
||||||
|
@app.route('/api/todos', methods=['POST'])
|
||||||
|
@cross_origin()
|
||||||
|
def create_todo():
|
||||||
|
content = request.json.get('content')
|
||||||
|
degree = request.json.get('degree')
|
||||||
|
timestamp = datetime.now()
|
||||||
|
todo_uuid = str(uuid.uuid4())
|
||||||
|
result = todos.insert_one({
|
||||||
|
'content': content,
|
||||||
|
'degree': degree,
|
||||||
|
'timestamp': timestamp,
|
||||||
|
'uuid': todo_uuid
|
||||||
|
})
|
||||||
|
return jsonify({'id': todo_uuid}), 201
|
||||||
|
|
||||||
|
@app.route('/api/todos/<uuid>', methods=['DELETE'])
|
||||||
|
@cross_origin()
|
||||||
|
def delete_todo(uuid):
|
||||||
|
result = todos.delete_one({"uuid": uuid})
|
||||||
|
if result.deleted_count > 0:
|
||||||
|
return '', 204
|
||||||
|
return jsonify({'error': 'Todo not found'}), 404
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(host='127.0.0.1', debug=True)
|
||||||
5
src/frontend/next-env.d.ts
vendored
Normal file
5
src/frontend/next-env.d.ts
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
/// <reference types="next" />
|
||||||
|
/// <reference types="next/image-types/global" />
|
||||||
|
|
||||||
|
// NOTE: This file should not be edited
|
||||||
|
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||||
2205
src/frontend/package-lock.json
generated
Normal file
2205
src/frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
27
src/frontend/package.json
Normal file
27
src/frontend/package.json
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"name": "pyxr-frontend",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"proxy": "http://localhost:5000",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"lint": "next lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"next": "14.0.4",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"axios": "^1.6.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.10.4",
|
||||||
|
"@types/react": "^18.2.45",
|
||||||
|
"@types/react-dom": "^18.2.17",
|
||||||
|
"typescript": "^5.3.3",
|
||||||
|
"autoprefixer": "^10.4.16",
|
||||||
|
"postcss": "^8.4.32",
|
||||||
|
"tailwindcss": "^3.3.6"
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/frontend/src/app/layout.tsx
Normal file
16
src/frontend/src/app/layout.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
export const metadata = {
|
||||||
|
title: 'Next.js',
|
||||||
|
description: 'Generated by Next.js',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<html lang="en">
|
||||||
|
<body>{children}</body>
|
||||||
|
</html>
|
||||||
|
)
|
||||||
|
}
|
||||||
128
src/frontend/src/app/page.tsx
Normal file
128
src/frontend/src/app/page.tsx
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
interface Todo {
|
||||||
|
uuid: string;
|
||||||
|
content: string;
|
||||||
|
degree: 'Important' | 'Unimportant';
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
const [todos, setTodos] = useState<Todo[]>([]);
|
||||||
|
const [content, setContent] = useState('');
|
||||||
|
const [degree, setDegree] = useState<'Important' | 'Unimportant'>('Important');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchTodos();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const fetchTodos = async () => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get('http://localhost:5000/api/todos');
|
||||||
|
setTodos(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching todos:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
try {
|
||||||
|
await axios.post('http://localhost:5000/api/todos', { content, degree });
|
||||||
|
setContent('');
|
||||||
|
setDegree('Important');
|
||||||
|
fetchTodos();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating todo:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = async (uuid: string) => {
|
||||||
|
if (!confirm('Are you sure you want to delete this entry?')) return;
|
||||||
|
try {
|
||||||
|
await axios.delete(`http://localhost:5000/api/todos/${uuid}`);
|
||||||
|
fetchTodos();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting todo:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="max-w-4xl mx-auto p-4">
|
||||||
|
<h1 className="text-3xl font-bold mb-6">FlaskTODO</h1>
|
||||||
|
<hr className="mb-6" />
|
||||||
|
|
||||||
|
<form onSubmit={handleSubmit} className="mb-8 space-y-4">
|
||||||
|
<div>
|
||||||
|
<label htmlFor="content" className="block font-bold mb-2">
|
||||||
|
Todo content
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="content"
|
||||||
|
value={content}
|
||||||
|
onChange={(e) => setContent(e.target.value)}
|
||||||
|
placeholder="Todo Content"
|
||||||
|
className="w-full p-2 border rounded"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<p className="font-bold">Degree</p>
|
||||||
|
<div className="space-x-4">
|
||||||
|
<label className="inline-flex items-center">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="degree"
|
||||||
|
value="Important"
|
||||||
|
checked={degree === 'Important'}
|
||||||
|
onChange={(e) => setDegree(e.target.value as 'Important')}
|
||||||
|
className="mr-2"
|
||||||
|
/>
|
||||||
|
Important
|
||||||
|
</label>
|
||||||
|
<label className="inline-flex items-center">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="degree"
|
||||||
|
value="Unimportant"
|
||||||
|
checked={degree === 'Unimportant'}
|
||||||
|
onChange={(e) => setDegree(e.target.value as 'Unimportant')}
|
||||||
|
className="mr-2"
|
||||||
|
/>
|
||||||
|
Unimportant
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<hr className="mb-6" />
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
{todos.map((todo) => (
|
||||||
|
<div key={todo.uuid} className="bg-gray-100 p-4 rounded">
|
||||||
|
<p>
|
||||||
|
[{todo.uuid}]: {todo.content} <i>({todo.degree})</i>
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
onClick={() => handleDelete(todo.uuid)}
|
||||||
|
className="mt-2 bg-red-500 text-white px-3 py-1 rounded text-sm hover:bg-red-600"
|
||||||
|
>
|
||||||
|
Delete Todo
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
27
src/frontend/tsconfig.json
Normal file
27
src/frontend/tsconfig.json
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"incremental": true,
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "next"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
69
tests/test_app.py
Normal file
69
tests/test_app.py
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
import pytest
|
||||||
|
from pymongo import MongoClient
|
||||||
|
from pymongo.errors import ConnectionFailure # Import ConnectionFailure
|
||||||
|
from datetime import datetime
|
||||||
|
from bson.objectid import ObjectId
|
||||||
|
from pyxr.app import app
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def client():
|
||||||
|
app.config['TESTING'] = True
|
||||||
|
with app.test_client() as client:
|
||||||
|
yield client
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mongo_client():
|
||||||
|
# 使用测试数据库
|
||||||
|
client = MongoClient('localhost', 27017)
|
||||||
|
db = client.test_flask_db
|
||||||
|
todos = db.todos
|
||||||
|
yield todos
|
||||||
|
# 清理测试数据
|
||||||
|
todos.delete_many({})
|
||||||
|
client.close()
|
||||||
|
|
||||||
|
def test_get_todos_empty(client, mongo_client):
|
||||||
|
# 测试空数据库情况
|
||||||
|
response = client.get('/api/todos')
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json == []
|
||||||
|
|
||||||
|
def test_create_todo(client, mongo_client):
|
||||||
|
# 测试创建待办事项
|
||||||
|
test_todo = {
|
||||||
|
'content': '测试待办事项',
|
||||||
|
'degree': 1
|
||||||
|
}
|
||||||
|
response = client.post('/api/todos', json=test_todo)
|
||||||
|
assert response.status_code == 201
|
||||||
|
assert '_id' in response.json
|
||||||
|
|
||||||
|
# 验证数据是否正确保存
|
||||||
|
saved_todo = mongo_client.find_one({'_id': ObjectId(response.json['_id'])})
|
||||||
|
assert saved_todo is not None
|
||||||
|
assert saved_todo['content'] == test_todo['content']
|
||||||
|
assert saved_todo['degree'] == test_todo['degree']
|
||||||
|
assert 'timestamp' in saved_todo
|
||||||
|
|
||||||
|
def test_delete_todo(client, mongo_client):
|
||||||
|
# 先创建一个待办事项
|
||||||
|
todo = {
|
||||||
|
'content': '要删除的待办事项',
|
||||||
|
'degree': 1,
|
||||||
|
'timestamp': datetime.now()
|
||||||
|
}
|
||||||
|
result = mongo_client.insert_one(todo)
|
||||||
|
todo_id = str(result.inserted_id)
|
||||||
|
|
||||||
|
# 测试删除
|
||||||
|
response = client.delete(f'/api/todos/{todo_id}')
|
||||||
|
assert response.status_code == 204
|
||||||
|
|
||||||
|
# 验证是否已删除
|
||||||
|
assert mongo_client.find_one({'_id': ObjectId(todo_id)}) is None
|
||||||
|
|
||||||
|
def test_delete_nonexistent_todo(client):
|
||||||
|
# 测试删除不存在的待办事项
|
||||||
|
fake_id = str(ObjectId())
|
||||||
|
response = client.delete(f'/api/todos/{fake_id}')
|
||||||
|
assert response.status_code == 204 # 即使不存在也返回204
|
||||||
42
tests/test_mongodb_health.py
Normal file
42
tests/test_mongodb_health.py
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import pytest
|
||||||
|
from pymongo import MongoClient
|
||||||
|
from pymongo.errors import ConnectionFailure
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def test_mongo_client():
|
||||||
|
client = MongoClient('localhost', 27017)
|
||||||
|
try:
|
||||||
|
# 测试连接是否成功
|
||||||
|
client.admin.command('ping')
|
||||||
|
db = client.test_health_check_db
|
||||||
|
collection = db.test_collection
|
||||||
|
yield collection
|
||||||
|
except ConnectionError:
|
||||||
|
pytest.fail("MongoDB连接失败 - 请确保MongoDB服务正在运行")
|
||||||
|
finally:
|
||||||
|
# 清理测试数据
|
||||||
|
if 'test_health_check_db' in client.list_database_names():
|
||||||
|
client.drop_database('test_health_check_db')
|
||||||
|
client.close()
|
||||||
|
|
||||||
|
def test_mongodb_connection_and_operations(test_mongo_client):
|
||||||
|
# 测试数据插入
|
||||||
|
test_doc = {"test_key": "test_value"}
|
||||||
|
insert_result = test_mongo_client.insert_one(test_doc)
|
||||||
|
assert insert_result.inserted_id is not None
|
||||||
|
|
||||||
|
# 测试数据查询
|
||||||
|
found_doc = test_mongo_client.find_one({"test_key": "test_value"})
|
||||||
|
assert found_doc is not None
|
||||||
|
assert found_doc["test_key"] == "test_value"
|
||||||
|
|
||||||
|
# 测试数据更新
|
||||||
|
update_result = test_mongo_client.update_one(
|
||||||
|
{"test_key": "test_value"},
|
||||||
|
{"$set": {"test_key": "updated_value"}}
|
||||||
|
)
|
||||||
|
assert update_result.modified_count == 1
|
||||||
|
|
||||||
|
# 测试数据删除
|
||||||
|
delete_result = test_mongo_client.delete_one({"test_key": "updated_value"})
|
||||||
|
assert delete_result.deleted_count == 1
|
||||||
Loading…
Add table
Reference in a new issue