/*
 * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.micode.notes.data;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.*;

/**
 * 文件夹数据库操作测试类
 *
 * 测试文件夹的创建、读取、更新、删除等数据库操作
 * 测试文件夹树查询、notes_count维护、系统文件夹保护等功能
 */
public class FolderDatabaseTest {

    /**
     * 测试数据库实例（内存数据库）
     */
    private SQLiteDatabase mDatabase;

    /**
     * 测试用的数据库帮助类
     */
    private NotesDatabaseHelper mHelper;

    /**
     * 测试前的初始化
     * 创建内存数据库，初始化表结构和系统文件夹
     */
    @Before
    public void setUp() {
        // 创建内存数据库用于测试
        mDatabase = SQLiteDatabase.openDatabase(":memory:", null,
                SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.CREATE_IF_NECESSARY);

        // 创建数据库帮助类（但不使用其数据库实例）
        mHelper = new NotesDatabaseHelper(null);

        // 手动创建表结构和触发器
        mHelper.createNoteTable(mDatabase);
        mHelper.createDataTable(mDatabase);
    }

    /**
     * 测试后的清理
     * 关闭数据库连接
     */
    @After
    public void tearDown() {
        if (mDatabase != null && mDatabase.isOpen()) {
            mDatabase.close();
        }
    }

    // ==================== 测试1：文件夹CRUD操作 ====================

    /**
     * 测试创建文件夹
     * 验证能够成功创建文件夹，并且返回的ID大于0
     */
    @Test
    public void testCreateFolder() {
        // 准备测试数据
        ContentValues values = new ContentValues();
        values.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
        values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        values.put(Notes.NoteColumns.SNIPPET, "测试文件夹");

        // 执行插入
        long folderId = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, values);

        // 验证结果
        assertTrue("文件夹ID应该大于0", folderId > 0);

        // 验证数据是否正确插入
        Cursor cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                null,
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folderId)},
                null, null, null
        );

        assertNotNull("查询结果不应该为null", cursor);
        assertTrue("应该找到一条记录", cursor.moveToFirst());
        assertEquals("类型应该为文件夹", Notes.TYPE_FOLDER, cursor.getInt(cursor.getColumnIndexOrThrow(Notes.NoteColumns.TYPE)));
        assertEquals("名称应该正确", "测试文件夹", cursor.getString(cursor.getColumnIndexOrThrow(Notes.NoteColumns.SNIPPET)));
        assertEquals("父文件夹ID应该正确", Notes.ID_ROOT_FOLDER, cursor.getLong(cursor.getColumnIndexOrThrow(Notes.NoteColumns.PARENT_ID)));

        cursor.close();
    }

    /**
     * 测试读取文件夹
     * 验证能够正确读取文件夹信息
     */
    @Test
    public void testReadFolder() {
        // 先创建一个文件夹
        ContentValues values = new ContentValues();
        values.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
        values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        values.put(Notes.NoteColumns.SNIPPET, "测试文件夹");
        long folderId = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, values);

        // 读取文件夹
        Cursor cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                null,
                Notes.NoteColumns.ID + "=? AND " + Notes.NoteColumns.TYPE + "=?",
                new String[]{String.valueOf(folderId), String.valueOf(Notes.TYPE_FOLDER)},
                null, null, null
        );

        assertNotNull("查询结果不应该为null", cursor);
        assertTrue("应该找到一条记录", cursor.moveToFirst());

        // 验证数据
        assertEquals("ID应该匹配", folderId, cursor.getLong(cursor.getColumnIndexOrThrow(Notes.NoteColumns.ID)));
        assertEquals("名称应该正确", "测试文件夹", cursor.getString(cursor.getColumnIndexOrThrow(Notes.NoteColumns.SNIPPET)));

        cursor.close();
    }

    /**
     * 测试更新文件夹
     * 验证能够成功更新文件夹名称
     */
    @Test
    public void testUpdateFolder() {
        // 先创建一个文件夹
        ContentValues values = new ContentValues();
        values.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
        values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        values.put(Notes.NoteColumns.SNIPPET, "测试文件夹");
        long folderId = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, values);

        // 更新文件夹
        ContentValues updateValues = new ContentValues();
        updateValues.put(Notes.NoteColumns.SNIPPET, "更新后的文件夹名称");
        int updated = mDatabase.update(
                NotesDatabaseHelper.TABLE.NOTE,
                updateValues,
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folderId)}
        );

        // 验证更新
        assertEquals("应该更新1条记录", 1, updated);

        // 验证数据是否正确更新
        Cursor cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                null,
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folderId)},
                null, null, null
        );

        assertTrue("应该找到一条记录", cursor.moveToFirst());
        assertEquals("名称应该已更新", "更新后的文件夹名称", cursor.getString(cursor.getColumnIndexOrThrow(Notes.NoteColumns.SNIPPET)));

        cursor.close();
    }

    /**
     * 测试删除文件夹（物理删除）
     * 验证能够成功删除文件夹
     */
    @Test
    public void testDeleteFolder() {
        // 先创建一个文件夹
        ContentValues values = new ContentValues();
        values.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
        values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        values.put(Notes.NoteColumns.SNIPPET, "测试文件夹");
        long folderId = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, values);

        // 删除文件夹
        int deleted = mDatabase.delete(
                NotesDatabaseHelper.TABLE.NOTE,
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folderId)}
        );

        // 验证删除
        assertEquals("应该删除1条记录", 1, deleted);

        // 验证数据是否已删除
        Cursor cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                null,
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folderId)},
                null, null, null
        );

        assertFalse("不应该找到任何记录", cursor.moveToFirst());
        cursor.close();
    }

    /**
     * 测试文件夹名称不能为空
     * 验证当尝试创建空名称的文件夹时，应该失败或使用默认值
     */
    @Test
    public void testFolderNameCannotBeNull() {
        // 尝试创建名称为空的文件夹
        ContentValues values = new ContentValues();
        values.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
        values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        values.put(Notes.NoteColumns.SNIPPET, ""); // 空字符串

        // 尝试插入
        long folderId = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, values);

        // SQLite允许空字符串，但应该使用默认值
        assertTrue("文件夹ID应该大于0", folderId > 0);

        // 验证数据
        Cursor cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                null,
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folderId)},
                null, null, null
        );

        assertTrue("应该找到一条记录", cursor.moveToFirst());
        // 数据库定义了DEFAULT ''，所以应该接受空字符串
        assertEquals("名称应该为空字符串", "", cursor.getString(cursor.getColumnIndexOrThrow(Notes.NoteColumns.SNIPPET)));

        cursor.close();
    }

    /**
     * 测试创建嵌套文件夹
     * 验证能够创建多级嵌套的文件夹结构
     */
    @Test
    public void testCreateNestedFolder() {
        // 创建第一级文件夹
        ContentValues folder1 = new ContentValues();
        folder1.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
        folder1.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folder1.put(Notes.NoteColumns.SNIPPET, "一级文件夹");
        long folder1Id = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folder1);

        // 创建第二级文件夹
        ContentValues folder2 = new ContentValues();
        folder2.put(Notes.NoteColumns.PARENT_ID, folder1Id);
        folder2.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folder2.put(Notes.NoteColumns.SNIPPET, "二级文件夹");
        long folder2Id = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folder2);

        // 创建第三级文件夹
        ContentValues folder3 = new ContentValues();
        folder3.put(Notes.NoteColumns.PARENT_ID, folder2Id);
        folder3.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folder3.put(Notes.NoteColumns.SNIPPET, "三级文件夹");
        long folder3Id = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folder3);

        // 验证所有文件夹都创建成功
        assertTrue("一级文件夹ID应该大于0", folder1Id > 0);
        assertTrue("二级文件夹ID应该大于0", folder2Id > 0);
        assertTrue("三级文件夹ID应该大于0", folder3Id > 0);

        // 验证层级关系
        Cursor cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                null,
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folder2Id)},
                null, null, null
        );

        assertTrue("应该找到二级文件夹", cursor.moveToFirst());
        assertEquals("二级文件夹的父ID应该是一级文件夹", folder1Id, cursor.getLong(cursor.getColumnIndexOrThrow(Notes.NoteColumns.PARENT_ID)));

        cursor.close();
    }

    /**
     * 测试查询特定父文件夹下的所有文件夹
     * 验证能够正确查询某个文件夹下的所有子文件夹
     */
    @Test
    public void testQueryFoldersByParentId() {
        // 在根文件夹下创建多个子文件夹
        for (int i = 0; i < 5; i++) {
            ContentValues values = new ContentValues();
            values.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
            values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
            values.put(Notes.NoteColumns.SNIPPET, "文件夹" + i);
            mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, values);
        }

        // 查询根文件夹下的所有文件夹
        Cursor cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                null,
                Notes.NoteColumns.PARENT_ID + "=? AND " + Notes.NoteColumns.TYPE + "=?",
                new String[]{String.valueOf(Notes.ID_ROOT_FOLDER), String.valueOf(Notes.TYPE_FOLDER)},
                null, null, null
        );

        assertNotNull("查询结果不应该为null", cursor);
        assertEquals("应该找到5个文件夹", 5, cursor.getCount());

        // 验证每个文件夹的父ID都是根文件夹
        while (cursor.moveToNext()) {
            assertEquals("父ID应该都是根文件夹", Notes.ID_ROOT_FOLDER, cursor.getLong(cursor.getColumnIndexOrThrow(Notes.NoteColumns.PARENT_ID)));
            assertEquals("类型应该都是文件夹", Notes.TYPE_FOLDER, cursor.getInt(cursor.getColumnIndexOrThrow(Notes.NoteColumns.TYPE)));
        }

        cursor.close();
    }

    /**
     * 测试查询所有文件夹（不包含系统文件夹）
     * 验证能够查询所有用户创建的文件夹
     */
    @Test
    public void testQueryAllUserFolders() {
        // 创建多个用户文件夹
        for (int i = 0; i < 3; i++) {
            ContentValues values = new ContentValues();
            values.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
            values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
            values.put(Notes.NoteColumns.SNIPPET, "用户文件夹" + i);
            mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, values);
        }

        // 查询所有文件夹（排除系统文件夹）
        Cursor cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                null,
                Notes.NoteColumns.TYPE + "=? AND " + Notes.NoteColumns.ID + ">0",
                new String[]{String.valueOf(Notes.TYPE_FOLDER)},
                null, null, null
        );

        assertNotNull("查询结果不应该为null", cursor);
        assertEquals("应该找到3个用户文件夹", 3, cursor.getCount());

        cursor.close();
    }

    // ==================== 测试2：notes_count维护 ====================

    /**
     * 测试插入笔记时增加文件夹的notes_count
     * 验证触发器是否正确维护notes_count
     */
    @Test
    public void testIncreaseNotesCountOnInsert() {
        // 创建一个文件夹
        ContentValues folderValues = new ContentValues();
        folderValues.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
        folderValues.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folderValues.put(Notes.NoteColumns.SNIPPET, "测试文件夹");
        long folderId = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folderValues);

        // 验证初始notes_count为0
        Cursor folderCursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                new String[]{Notes.NoteColumns.NOTES_COUNT},
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folderId)},
                null, null, null
        );
        assertTrue("应该找到文件夹", folderCursor.moveToFirst());
        assertEquals("初始notes_count应该为0", 0, folderCursor.getInt(0));
        folderCursor.close();

        // 向该文件夹插入笔记
        ContentValues noteValues = new ContentValues();
        noteValues.put(Notes.NoteColumns.PARENT_ID, folderId);
        noteValues.put(Notes.NoteColumns.TYPE, Notes.TYPE_NOTE);
        noteValues.put(Notes.NoteColumns.SNIPPET, "测试笔记");
        mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, noteValues);

        // 验证notes_count增加
        folderCursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                new String[]{Notes.NoteColumns.NOTES_COUNT},
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folderId)},
                null, null, null
        );
        assertTrue("应该找到文件夹", folderCursor.moveToFirst());
        assertEquals("notes_count应该增加到1", 1, folderCursor.getInt(0));
        folderCursor.close();

        // 插入第二条笔记
        noteValues.clear();
        noteValues.put(Notes.NoteColumns.PARENT_ID, folderId);
        noteValues.put(Notes.NoteColumns.TYPE, Notes.TYPE_NOTE);
        noteValues.put(Notes.NoteColumns.SNIPPET, "测试笔记2");
        mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, noteValues);

        // 验证notes_count增加到2
        folderCursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                new String[]{Notes.NoteColumns.NOTES_COUNT},
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folderId)},
                null, null, null
        );
        assertTrue("应该找到文件夹", folderCursor.moveToFirst());
        assertEquals("notes_count应该增加到2", 2, folderCursor.getInt(0));
        folderCursor.close();
    }

    /**
     * 测试删除笔记时减少文件夹的notes_count
     * 验证触发器是否正确维护notes_count
     */
    @Test
    public void testDecreaseNotesCountOnDelete() {
        // 创建一个文件夹
        ContentValues folderValues = new ContentValues();
        folderValues.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
        folderValues.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folderValues.put(Notes.NoteColumns.SNIPPET, "测试文件夹");
        long folderId = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folderValues);

        // 向该文件夹插入3条笔记
        for (int i = 0; i < 3; i++) {
            ContentValues noteValues = new ContentValues();
            noteValues.put(Notes.NoteColumns.PARENT_ID, folderId);
            noteValues.put(Notes.NoteColumns.TYPE, Notes.TYPE_NOTE);
            noteValues.put(Notes.NoteColumns.SNIPPET, "测试笔记" + i);
            mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, noteValues);
        }

        // 验证notes_count为3
        Cursor folderCursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                new String[]{Notes.NoteColumns.NOTES_COUNT},
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folderId)},
                null, null, null
        );
        assertTrue("应该找到文件夹", folderCursor.moveToFirst());
        assertEquals("notes_count应该为3", 3, folderCursor.getInt(0));
        folderCursor.close();

        // 删除一条笔记
        // 先查询笔记ID
        Cursor noteCursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                new String[]{Notes.NoteColumns.ID},
                Notes.NoteColumns.PARENT_ID + "=? AND " + Notes.NoteColumns.TYPE + "=?",
                new String[]{String.valueOf(folderId), String.valueOf(Notes.TYPE_NOTE)},
                null, null, null
        );
        assertTrue("应该找到笔记", noteCursor.moveToFirst());
        long noteId = noteCursor.getLong(0);
        noteCursor.close();

        // 删除笔记
        mDatabase.delete(
                NotesDatabaseHelper.TABLE.NOTE,
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(noteId)}
        );

        // 验证notes_count减少到2
        folderCursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                new String[]{Notes.NoteColumns.NOTES_COUNT},
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folderId)},
                null, null, null
        );
        assertTrue("应该找到文件夹", folderCursor.moveToFirst());
        assertEquals("notes_count应该减少到2", 2, folderCursor.getInt(0));
        folderCursor.close();
    }

    /**
     * 测试笔记移动时更新两个文件夹的notes_count
     * 验证笔记从一个文件夹移动到另一个文件夹时，两个文件夹的notes_count都正确更新
     */
    @Test
    public void testUpdateNotesCountOnMove() {
        // 创建两个文件夹
        ContentValues folder1Values = new ContentValues();
        folder1Values.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
        folder1Values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folder1Values.put(Notes.NoteColumns.SNIPPET, "文件夹1");
        long folder1Id = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folder1Values);

        ContentValues folder2Values = new ContentValues();
        folder2Values.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
        folder2Values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folder2Values.put(Notes.NoteColumns.SNIPPET, "文件夹2");
        long folder2Id = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folder2Values);

        // 向文件夹1插入2条笔记
        for (int i = 0; i < 2; i++) {
            ContentValues noteValues = new ContentValues();
            noteValues.put(Notes.NoteColumns.PARENT_ID, folder1Id);
            noteValues.put(Notes.NoteColumns.TYPE, Notes.TYPE_NOTE);
            noteValues.put(Notes.NoteColumns.SNIPPET, "测试笔记" + i);
            mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, noteValues);
        }

        // 验证文件夹1的notes_count为2，文件夹2为0
        Cursor folder1Cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                new String[]{Notes.NoteColumns.NOTES_COUNT},
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folder1Id)},
                null, null, null
        );
        assertTrue("应该找到文件夹1", folder1Cursor.moveToFirst());
        assertEquals("文件夹1的notes_count应该为2", 2, folder1Cursor.getInt(0));
        folder1Cursor.close();

        Cursor folder2Cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                new String[]{Notes.NoteColumns.NOTES_COUNT},
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folder2Id)},
                null, null, null
        );
        assertTrue("应该找到文件夹2", folder2Cursor.moveToFirst());
        assertEquals("文件夹2的notes_count应该为0", 0, folder2Cursor.getInt(0));
        folder2Cursor.close();

        // 查询笔记ID
        Cursor noteCursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                new String[]{Notes.NoteColumns.ID},
                Notes.NoteColumns.PARENT_ID + "=? AND " + Notes.NoteColumns.TYPE + "=?",
                new String[]{String.valueOf(folder1Id), String.valueOf(Notes.TYPE_NOTE)},
                null, null, null
        );
        assertTrue("应该找到笔记", noteCursor.moveToFirst());
        long noteId = noteCursor.getLong(0);
        noteCursor.close();

        // 移动笔记：从文件夹1移动到文件夹2
        ContentValues updateValues = new ContentValues();
        updateValues.put(Notes.NoteColumns.PARENT_ID, folder2Id);
        mDatabase.update(
                NotesDatabaseHelper.TABLE.NOTE,
                updateValues,
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(noteId)}
        );

        // 验证文件夹1的notes_count减少到1
        folder1Cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                new String[]{Notes.NoteColumns.NOTES_COUNT},
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folder1Id)},
                null, null, null
        );
        assertTrue("应该找到文件夹1", folder1Cursor.moveToFirst());
        assertEquals("文件夹1的notes_count应该减少到1", 1, folder1Cursor.getInt(0));
        folder1Cursor.close();

        // 验证文件夹2的notes_count增加到1
        folder2Cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                new String[]{Notes.NoteColumns.NOTES_COUNT},
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folder2Id)},
                null, null, null
        );
        assertTrue("应该找到文件夹2", folder2Cursor.moveToFirst());
        assertEquals("文件夹2的notes_count应该增加到1", 1, folder2Cursor.getInt(0));
        folder2Cursor.close();
    }

    /**
     * 测试删除文件夹时不会触发级联删除笔记的notes_count更新
     * 验证删除文件夹时，笔记的级联删除不影响其他文件夹的notes_count
     */
    @Test
    public void testDeleteFolderDoesNotAffectOtherFoldersNotesCount() {
        // 创建文件夹1
        ContentValues folder1Values = new ContentValues();
        folder1Values.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
        folder1Values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folder1Values.put(Notes.NoteColumns.SNIPPET, "文件夹1");
        long folder1Id = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folder1Values);

        // 创建文件夹2（文件夹1的子文件夹）
        ContentValues folder2Values = new ContentValues();
        folder2Values.put(Notes.NoteColumns.PARENT_ID, folder1Id);
        folder2Values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folder2Values.put(Notes.NoteColumns.SNIPPET, "文件夹2");
        long folder2Id = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folder2Values);

        // 向文件夹2插入笔记
        ContentValues noteValues = new ContentValues();
        noteValues.put(Notes.NoteColumns.PARENT_ID, folder2Id);
        noteValues.put(Notes.NoteColumns.TYPE, Notes.TYPE_NOTE);
        noteValues.put(Notes.NoteColumns.SNIPPET, "测试笔记");
        mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, noteValues);

        // 验证文件夹2的notes_count为1
        Cursor folder2Cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                new String[]{Notes.NoteColumns.NOTES_COUNT},
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folder2Id)},
                null, null, null
        );
        assertTrue("应该找到文件夹2", folder2Cursor.moveToFirst());
        assertEquals("文件夹2的notes_count应该为1", 1, folder2Cursor.getInt(0));
        folder2Cursor.close();

        // 删除文件夹1（应该级联删除文件夹2及其笔记）
        mDatabase.delete(
                NotesDatabaseHelper.TABLE.NOTE,
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folder1Id)}
        );

        // 验证文件夹2和笔记都已被删除
        folder2Cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                new String[]{Notes.NoteColumns.ID},
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folder2Id)},
                null, null, null
        );
        assertFalse("文件夹2应该已被删除", folder2Cursor.moveToFirst());
        folder2Cursor.close();
    }

    /**
     * 测试移动文件夹时不影响notes_count
     * 验证移动文件夹时，notes_count保持不变
     */
    @Test
    public void testMoveFolderDoesNotChangeNotesCount() {
        // 创建文件夹1
        ContentValues folder1Values = new ContentValues();
        folder1Values.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
        folder1Values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folder1Values.put(Notes.NoteColumns.SNIPPET, "文件夹1");
        long folder1Id = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folder1Values);

        // 创建文件夹2
        ContentValues folder2Values = new ContentValues();
        folder2Values.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
        folder2Values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folder2Values.put(Notes.NoteColumns.SNIPPET, "文件夹2");
        long folder2Id = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folder2Values);

        // 向文件夹1插入笔记
        ContentValues noteValues = new ContentValues();
        noteValues.put(Notes.NoteColumns.PARENT_ID, folder1Id);
        noteValues.put(Notes.NoteColumns.TYPE, Notes.TYPE_NOTE);
        noteValues.put(Notes.NoteColumns.SNIPPET, "测试笔记");
        mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, noteValues);

        // 验证文件夹1的notes_count为1
        Cursor folder1Cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                new String[]{Notes.NoteColumns.NOTES_COUNT},
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folder1Id)},
                null, null, null
        );
        assertTrue("应该找到文件夹1", folder1Cursor.moveToFirst());
        assertEquals("文件夹1的notes_count应该为1", 1, folder1Cursor.getInt(0));
        folder1Cursor.close();

        // 将文件夹1移动到文件夹2下
        ContentValues updateValues = new ContentValues();
        updateValues.put(Notes.NoteColumns.PARENT_ID, folder2Id);
        mDatabase.update(
                NotesDatabaseHelper.TABLE.NOTE,
                updateValues,
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folder1Id)}
        );

        // 验证文件夹1的notes_count仍为1
        folder1Cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                new String[]{Notes.NoteColumns.NOTES_COUNT},
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folder1Id)},
                null, null, null
        );
        assertTrue("应该找到文件夹1", folder1Cursor.moveToFirst());
        assertEquals("文件夹1的notes_count应该仍为1", 1, folder1Cursor.getInt(0));
        folder1Cursor.close();
    }

    // ==================== 测试3：系统文件夹保护 ====================

    /**
     * 测试不能删除系统文件夹
     * 验证系统文件夹（ID <= 0）不能被删除
     */
    @Test
    public void testCannotDeleteSystemFolder() {
        // 尝试删除根文件夹
        int deleted = mDatabase.delete(
                NotesDatabaseHelper.TABLE.NOTE,
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(Notes.ID_ROOT_FOLDER)}
        );

        // 验证删除失败
        assertEquals("不应该删除任何记录", 0, deleted);

        // 验证根文件夹仍然存在
        Cursor cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                null,
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(Notes.ID_ROOT_FOLDER)},
                null, null, null
        );
        assertTrue("根文件夹应该仍然存在", cursor.moveToFirst());
        cursor.close();

        // 尝试删除回收站
        deleted = mDatabase.delete(
                NotesDatabaseHelper.TABLE.NOTE,
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(Notes.ID_TRASH_FOLER)}
        );

        // 验证删除失败
        assertEquals("不应该删除回收站", 0, deleted);
    }

    /**
     * 测试系统文件夹的type字段
     * 验证系统文件夹的type为TYPE_SYSTEM
     */
    @Test
    public void testSystemFolderType() {
        // 验证根文件夹的类型
        Cursor cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                new String[]{Notes.NoteColumns.TYPE},
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(Notes.ID_ROOT_FOLDER)},
                null, null, null
        );
        assertTrue("应该找到根文件夹", cursor.moveToFirst());
        assertEquals("根文件夹的类型应该为TYPE_SYSTEM", Notes.TYPE_SYSTEM, cursor.getInt(0));
        cursor.close();

        // 验证回收站的类型
        cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                new String[]{Notes.NoteColumns.TYPE},
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(Notes.ID_TRASH_FOLER)},
                null, null, null
        );
        assertTrue("应该找到回收站", cursor.moveToFirst());
        assertEquals("回收站的类型应该为TYPE_SYSTEM", Notes.TYPE_SYSTEM, cursor.getInt(0));
        cursor.close();
    }

    /**
     * 测试系统文件夹不能被重命名
     * 验证即使尝试更新系统文件夹的名称，也应该失败或无效
     */
    @Test
    public void testCannotRenameSystemFolder() {
        // 尝试重命名根文件夹
        ContentValues updateValues = new ContentValues();
        updateValues.put(Notes.NoteColumns.SNIPPET, "新名称");
        int updated = mDatabase.update(
                NotesDatabaseHelper.TABLE.NOTE,
                updateValues,
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(Notes.ID_ROOT_FOLDER)}
        );

        // 验证更新（数据库层面允许更新，但应用层应该阻止）
        // 这里测试数据库层面
        assertTrue("数据库层面允许更新", updated > 0);

        // 验证名称已更改
        Cursor cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                new String[]{Notes.NoteColumns.SNIPPET},
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(Notes.ID_ROOT_FOLDER)},
                null, null, null
        );
        assertTrue("应该找到根文件夹", cursor.moveToFirst());
        assertEquals("名称应该已更改", "新名称", cursor.getString(0));
        cursor.close();

        // 注意：实际应用中，ContentProvider或业务层应该阻止此操作
    }

    // ==================== 测试4：回收站功能 ====================

    /**
     * 测试将便签移动到回收站
     * 验证便签的parent_id更新为回收站ID
     */
    @Test
    public void testMoveNoteToTrash() {
        // 创建一个文件夹
        ContentValues folderValues = new ContentValues();
        folderValues.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
        folderValues.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folderValues.put(Notes.NoteColumns.SNIPPET, "测试文件夹");
        long folderId = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folderValues);

        // 向文件夹插入笔记
        ContentValues noteValues = new ContentValues();
        noteValues.put(Notes.NoteColumns.PARENT_ID, folderId);
        noteValues.put(Notes.NoteColumns.TYPE, Notes.TYPE_NOTE);
        noteValues.put(Notes.NoteColumns.SNIPPET, "测试笔记");
        long noteId = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, noteValues);

        // 将笔记移动到回收站
        ContentValues updateValues = new ContentValues();
        updateValues.put(Notes.NoteColumns.PARENT_ID, Notes.ID_TRASH_FOLER);
        int updated = mDatabase.update(
                NotesDatabaseHelper.TABLE.NOTE,
                updateValues,
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(noteId)}
        );

        // 验证更新成功
        assertEquals("应该更新1条记录", 1, updated);

        // 验证笔记已在回收站中
        Cursor cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                null,
                Notes.NoteColumns.ID + "=? AND " + Notes.NoteColumns.PARENT_ID + "=?",
                new String[]{String.valueOf(noteId), String.valueOf(Notes.ID_TRASH_FOLER)},
                null, null, null
        );
        assertTrue("笔记应该在回收站中", cursor.moveToFirst());
        cursor.close();

        // 验证原文件夹的notes_count减少
        Cursor folderCursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                new String[]{Notes.NoteColumns.NOTES_COUNT},
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folderId)},
                null, null, null
        );
        assertTrue("应该找到文件夹", folderCursor.moveToFirst());
        assertEquals("文件夹的notes_count应该为0", 0, folderCursor.getInt(0));
        folderCursor.close();
    }

    /**
     * 测试将文件夹移动到回收站
     * 验证文件夹及其所有子项都移动到回收站
     */
    @Test
    public void testMoveFolderToTrash() {
        // 创建父文件夹
        ContentValues folder1Values = new ContentValues();
        folder1Values.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
        folder1Values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folder1Values.put(Notes.NoteColumns.SNIPPET, "文件夹1");
        long folder1Id = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folder1Values);

        // 创建子文件夹
        ContentValues folder2Values = new ContentValues();
        folder2Values.put(Notes.NoteColumns.PARENT_ID, folder1Id);
        folder2Values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folder2Values.put(Notes.NoteColumns.SNIPPET, "文件夹2");
        long folder2Id = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folder2Values);

        // 向子文件夹插入笔记
        ContentValues noteValues = new ContentValues();
        noteValues.put(Notes.NoteColumns.PARENT_ID, folder2Id);
        noteValues.put(Notes.NoteColumns.TYPE, Notes.TYPE_NOTE);
        noteValues.put(Notes.NoteColumns.SNIPPET, "测试笔记");
        mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, noteValues);

        // 将父文件夹移动到回收站
        ContentValues updateValues = new ContentValues();
        updateValues.put(Notes.NoteColumns.PARENT_ID, Notes.ID_TRASH_FOLER);
        mDatabase.update(
                NotesDatabaseHelper.TABLE.NOTE,
                updateValues,
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folder1Id)}
        );

        // 验证父文件夹在回收站中
        Cursor cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                null,
                Notes.NoteColumns.ID + "=? AND " + Notes.NoteColumns.PARENT_ID + "=?",
                new String[]{String.valueOf(folder1Id), String.valueOf(Notes.ID_TRASH_FOLER)},
                null, null, null
        );
        assertTrue("父文件夹应该在回收站中", cursor.moveToFirst());
        cursor.close();

        // 验证子文件夹也在回收站中
        cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                null,
                Notes.NoteColumns.ID + "=? AND " + Notes.NoteColumns.PARENT_ID + "=?",
                new String[]{String.valueOf(folder2Id), String.valueOf(Notes.ID_TRASH_FOLER)},
                null, null, null
        );
        assertTrue("子文件夹应该在回收站中", cursor.moveToFirst());
        cursor.close();

        // 验证笔记也在回收站中
        cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                null,
                Notes.NoteColumns.PARENT_ID + "=? AND " + Notes.NoteColumns.TYPE + "=?",
                new String[]{String.valueOf(Notes.ID_TRASH_FOLER), String.valueOf(Notes.TYPE_NOTE)},
                null, null, null
        );
        assertTrue("笔记应该在回收站中", cursor.moveToFirst());
        cursor.close();
    }

    /**
     * 测试从回收站恢复便签
     * 验证便签可以恢复到指定文件夹
     */
    @Test
    public void testRestoreNoteFromTrash() {
        // 创建文件夹
        ContentValues folderValues = new ContentValues();
        folderValues.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
        folderValues.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folderValues.put(Notes.NoteColumns.SNIPPET, "测试文件夹");
        long folderId = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folderValues);

        // 创建笔记并直接插入到回收站
        ContentValues noteValues = new ContentValues();
        noteValues.put(Notes.NoteColumns.PARENT_ID, Notes.ID_TRASH_FOLER);
        noteValues.put(Notes.NoteColumns.TYPE, Notes.TYPE_NOTE);
        noteValues.put(Notes.NoteColumns.SNIPPET, "回收站中的笔记");
        long noteId = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, noteValues);

        // 将笔记恢复到文件夹
        ContentValues updateValues = new ContentValues();
        updateValues.put(Notes.NoteColumns.PARENT_ID, folderId);
        int updated = mDatabase.update(
                NotesDatabaseHelper.TABLE.NOTE,
                updateValues,
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(noteId)}
        );

        // 验证更新成功
        assertEquals("应该更新1条记录", 1, updated);

        // 验证笔记已在文件夹中
        Cursor cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                null,
                Notes.NoteColumns.ID + "=? AND " + Notes.NoteColumns.PARENT_ID + "=?",
                new String[]{String.valueOf(noteId), String.valueOf(folderId)},
                null, null, null
        );
        assertTrue("笔记应该在文件夹中", cursor.moveToFirst());
        cursor.close();

        // 验证文件夹的notes_count增加
        Cursor folderCursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                new String[]{Notes.NoteColumns.NOTES_COUNT},
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folderId)},
                null, null, null
        );
        assertTrue("应该找到文件夹", folderCursor.moveToFirst());
        assertEquals("文件夹的notes_count应该为1", 1, folderCursor.getInt(0));
        folderCursor.close();
    }

    /**
     * 测试从回收站恢复文件夹
     * 验证文件夹及其子项可以恢复到指定位置
     */
    @Test
    public void testRestoreFolderFromTrash() {
        // 创建父文件夹并直接放入回收站
        ContentValues folder1Values = new ContentValues();
        folder1Values.put(Notes.NoteColumns.PARENT_ID, Notes.ID_TRASH_FOLER);
        folder1Values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folder1Values.put(Notes.NoteColumns.SNIPPET, "回收站中的文件夹");
        long folder1Id = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folder1Values);

        // 创建子文件夹
        ContentValues folder2Values = new ContentValues();
        folder2Values.put(Notes.NoteColumns.PARENT_ID, folder1Id);
        folder2Values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folder2Values.put(Notes.NoteColumns.SNIPPET, "子文件夹");
        long folder2Id = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folder2Values);

        // 向子文件夹插入笔记
        ContentValues noteValues = new ContentValues();
        noteValues.put(Notes.NoteColumns.PARENT_ID, folder2Id);
        noteValues.put(Notes.NoteColumns.TYPE, Notes.TYPE_NOTE);
        noteValues.put(Notes.NoteColumns.SNIPPET, "测试笔记");
        mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, noteValues);

        // 将父文件夹恢复到根目录
        ContentValues updateValues = new ContentValues();
        updateValues.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
        mDatabase.update(
                NotesDatabaseHelper.TABLE.NOTE,
                updateValues,
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folder1Id)}
        );

        // 验证父文件夹已恢复
        Cursor cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                null,
                Notes.NoteColumns.ID + "=? AND " + Notes.NoteColumns.PARENT_ID + "=?",
                new String[]{String.valueOf(folder1Id), String.valueOf(Notes.ID_ROOT_FOLDER)},
                null, null, null
        );
        assertTrue("父文件夹应该在根目录中", cursor.moveToFirst());
        cursor.close();

        // 验证子文件夹和笔记也跟随恢复（仍然在父文件夹下）
        cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                null,
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folder2Id)},
                null, null, null
        );
        assertTrue("子文件夹应该仍然存在", cursor.moveToFirst());
        assertEquals("子文件夹的父ID应该是父文件夹", folder1Id, cursor.getLong(cursor.getColumnIndexOrThrow(Notes.NoteColumns.PARENT_ID)));
        cursor.close();
    }

    /**
     * 测试查询回收站中的项目
     * 验证能够正确查询回收站中的所有项目
     */
    @Test
    public void testQueryTrashItems() {
        // 创建文件夹和笔记
        ContentValues folderValues = new ContentValues();
        folderValues.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
        folderValues.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folderValues.put(Notes.NoteColumns.SNIPPET, "测试文件夹");
        long folderId = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folderValues);

        ContentValues noteValues = new ContentValues();
        noteValues.put(Notes.NoteColumns.PARENT_ID, folderId);
        noteValues.put(Notes.NoteColumns.TYPE, Notes.TYPE_NOTE);
        noteValues.put(Notes.NoteColumns.SNIPPET, "测试笔记");
        mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, noteValues);

        // 将它们移动到回收站
        ContentValues updateValues = new ContentValues();
        updateValues.put(Notes.NoteColumns.PARENT_ID, Notes.ID_TRASH_FOLER);

        // 移动文件夹
        mDatabase.update(
                NotesDatabaseHelper.TABLE.NOTE,
                updateValues,
                Notes.NoteColumns.ID + "=?",
                new String[]{String.valueOf(folderId)}
        );

        // 查询回收站中的项目
        Cursor cursor = mDatabase.query(
                NotesDatabaseHelper.TABLE.NOTE,
                null,
                Notes.NoteColumns.PARENT_ID + "=?",
                new String[]{String.valueOf(Notes.ID_TRASH_FOLER)},
                null, null, null
        );

        assertNotNull("查询结果不应该为null", cursor);
        assertEquals("回收站中应该有2个项目", 2, cursor.getCount());

        // 排除系统文件夹（回收站本身）
        cursor.close();
    }

    // ==================== 测试5：循环依赖检测 ====================

    /**
     * 测试检测将文件夹移动到其子文件夹的循环依赖
     * 验证能够检测并阻止循环依赖
     */
    @Test
    public void testDetectCircularDependency() {
        // 创建父文件夹
        ContentValues folder1Values = new ContentValues();
        folder1Values.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
        folder1Values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folder1Values.put(Notes.NoteColumns.SNIPPET, "父文件夹");
        long folder1Id = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folder1Values);

        // 创建子文件夹
        ContentValues folder2Values = new ContentValues();
        folder2Values.put(Notes.NoteColumns.PARENT_ID, folder1Id);
        folder2Values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folder2Values.put(Notes.NoteColumns.SNIPPET, "子文件夹");
        long folder2Id = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folder2Values);

        // 创建孙子文件夹
        ContentValues folder3Values = new ContentValues();
        folder3Values.put(Notes.NoteColumns.PARENT_ID, folder2Id);
        folder3Values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folder3Values.put(Notes.NoteColumns.SNIPPET, "孙子文件夹");
        long folder3Id = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folder3Values);

        // 检测：尝试将父文件夹移动到孙子文件夹（应该检测到循环）
        boolean hasCircularDependency = hasCircularDependency(mDatabase, folder1Id, folder3Id);

        // 验证检测到循环
        assertTrue("应该检测到循环依赖", hasCircularDependency);

        // 检测：尝试将子文件夹移动到父文件夹（不应该检测到循环）
        hasCircularDependency = hasCircularDependency(mDatabase, folder2Id, folder1Id);

        // 验证没有检测到循环
        assertFalse("不应该检测到循环依赖", hasCircularDependency);

        // 检测：尝试将子文件夹移动到孙子文件夹（不应该检测到循环）
        hasCircularDependency = hasCircularDependency(mDatabase, folder2Id, folder3Id);

        // 验证没有检测到循环
        assertFalse("不应该检测到循环依赖", hasCircularDependency);
    }

    /**
     * 检测循环依赖的辅助方法
     * 递归检查目标文件夹是否为源文件夹的子节点
     *
     * @param db 数据库实例
     * @param sourceFolderId 源文件夹ID
     * @param targetFolderId 目标文件夹ID
     * @return 如果存在循环依赖返回true，否则返回false
     */
    private boolean hasCircularDependency(SQLiteDatabase db, long sourceFolderId, long targetFolderId) {
        // 递归检查目标文件夹的所有子文件夹
        return hasCircularDependencyRecursive(db, sourceFolderId, targetFolderId);
    }

    /**
     * 递归检查循环依赖
     *
     * @param db 数据库实例
     * @param sourceFolderId 源文件夹ID（正在移动的文件夹）
     * @param targetFolderId 目标文件夹ID（检查是否为源文件夹的子节点）
     * @return 如果目标文件夹是源文件夹的子节点返回true，否则返回false
     */
    private boolean hasCircularDependencyRecursive(SQLiteDatabase db, long sourceFolderId, long targetFolderId) {
        // 如果目标文件夹ID等于源文件夹ID，说明存在循环
        if (targetFolderId == sourceFolderId) {
            return true;
        }

        // 查询目标文件夹的所有子文件夹
        Cursor cursor = db.query(
                NotesDatabaseHelper.TABLE.NOTE,
                new String[]{Notes.NoteColumns.ID},
                Notes.NoteColumns.PARENT_ID + "=? AND " + Notes.NoteColumns.TYPE + "=?",
                new String[]{String.valueOf(targetFolderId), String.valueOf(Notes.TYPE_FOLDER)},
                null, null, null
        );

        boolean result = false;
        if (cursor.moveToFirst()) {
            do {
                long childFolderId = cursor.getLong(0);
                // 递归检查子文件夹
                if (hasCircularDependencyRecursive(db, sourceFolderId, childFolderId)) {
                    result = true;
                    break;
                }
            } while (cursor.moveToNext());
        }

        cursor.close();
        return result;
    }

    /**
     * 测试移动文件夹到根目录不会产生循环
     * 验证将任何文件夹移动到根目录都是安全的
     */
    @Test
    public void testMoveFolderToRootHasNoCircularDependency() {
        // 创建嵌套文件夹
        ContentValues folder1Values = new ContentValues();
        folder1Values.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
        folder1Values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folder1Values.put(Notes.NoteColumns.SNIPPET, "文件夹1");
        long folder1Id = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folder1Values);

        ContentValues folder2Values = new ContentValues();
        folder2Values.put(Notes.NoteColumns.PARENT_ID, folder1Id);
        folder2Values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folder2Values.put(Notes.NoteColumns.SNIPPET, "文件夹2");
        long folder2Id = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folder2Values);

        // 检测：将文件夹1移动到根目录（不应该检测到循环）
        boolean hasCircularDependency = hasCircularDependency(mDatabase, folder1Id, Notes.ID_ROOT_FOLDER);

        // 验证没有检测到循环
        assertFalse("移动到根目录不应该产生循环", hasCircularDependency);

        // 检测：将文件夹2移动到根目录（不应该检测到循环）
        hasCircularDependency = hasCircularDependency(mDatabase, folder2Id, Notes.ID_ROOT_FOLDER);

        // 验证没有检测到循环
        assertFalse("移动到根目录不应该产生循环", hasCircularDependency);
    }

    /**
     * 测试移动文件夹到同级目录不会产生循环
     * 验证将文件夹移动到同级目录是安全的
     */
    @Test
    public void testMoveFolderToSiblingHasNoCircularDependency() {
        // 创建父文件夹
        ContentValues parentFolderValues = new ContentValues();
        parentFolderValues.put(Notes.NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
        parentFolderValues.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        parentFolderValues.put(Notes.NoteColumns.SNIPPET, "父文件夹");
        long parentFolderId = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, parentFolderValues);

        // 创建两个子文件夹
        ContentValues folder1Values = new ContentValues();
        folder1Values.put(Notes.NoteColumns.PARENT_ID, parentFolderId);
        folder1Values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folder1Values.put(Notes.NoteColumns.SNIPPET, "文件夹1");
        long folder1Id = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folder1Values);

        ContentValues folder2Values = new ContentValues();
        folder2Values.put(Notes.NoteColumns.PARENT_ID, parentFolderId);
        folder2Values.put(Notes.NoteColumns.TYPE, Notes.TYPE_FOLDER);
        folder2Values.put(Notes.NoteColumns.SNIPPET, "文件夹2");
        long folder2Id = mDatabase.insert(NotesDatabaseHelper.TABLE.NOTE, null, folder2Values);

        // 检测：将文件夹1移动到文件夹2（不应该检测到循环）
        boolean hasCircularDependency = hasCircularDependency(mDatabase, folder1Id, folder2Id);

        // 验证没有检测到循环
        assertFalse("移动到同级文件夹不应该产生循环", hasCircularDependency);
    }
}
