test/cursors_test.c
/* cursors.c */

#include "test.h"
#include "mongo.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

void create_capped_collection( mongo *conn ) {
    bson b;

    bson_init( &b );
    bson_append_string( &b, "create", "cursors" );
    bson_append_bool( &b, "capped", 1 );
    bson_append_int( &b, "size", 1000000 );
    bson_finish( &b );

    ASSERT( mongo_run_command( conn, "test", &b, NULL ) == MONGO_OK );

    bson_destroy( &b );
}

void insert_sample_data( mongo *conn, int n ) {
    bson b;
    int i;

    for( i=0; i<n; i++ ) {
        bson_init( &b );
        bson_append_int( &b, "a", i );
        bson_finish( &b );

        mongo_insert( conn, "test.cursors", &b, NULL );

        bson_destroy( &b );
    }
}

void remove_sample_data( mongo *conn ) {
    mongo_cmd_drop_collection( conn, "test", "cursors", NULL );
}

int test_multiple_getmore( mongo *conn ) {
    mongo_cursor *cursor;
    int count;

    remove_sample_data( conn );
    create_capped_collection( conn );
    insert_sample_data( conn, 10000 );

    cursor = mongo_find( conn, "test.cursors", bson_shared_empty( ), bson_shared_empty( ), 0, 0, 0 );

    count = 0;
    while( mongo_cursor_next( cursor ) == MONGO_OK )
        count++;

    ASSERT( count == 10000 );

    ASSERT( mongo_cursor_next( cursor ) == MONGO_ERROR );
    ASSERT( cursor->err == MONGO_CURSOR_EXHAUSTED );

    mongo_cursor_destroy( cursor );
    remove_sample_data( conn );
    return 0;
}

int test_tailable( mongo *conn ) {
    mongo_cursor *cursor;
    bson b;
    int count;

    remove_sample_data( conn );
    create_capped_collection( conn );
    insert_sample_data( conn, 10000 );

    bson_init( &b );
    bson_append_start_object( &b, "$query" );
    bson_append_finish_object( &b );
    bson_append_start_object( &b, "$sort" );
    bson_append_int( &b, "$natural", -1 );
    bson_append_finish_object( &b );
    bson_finish( &b );

    cursor = mongo_find( conn, "test.cursors", &b, bson_shared_empty( ), 0, 0, MONGO_TAILABLE );
    bson_destroy( &b );

    count = 0;
    while( mongo_cursor_next( cursor ) == MONGO_OK )
        count++;

    ASSERT( count == 10000 );

    ASSERT( mongo_cursor_next( cursor ) == MONGO_ERROR );
    ASSERT( cursor->err == MONGO_CURSOR_PENDING );

    insert_sample_data( conn, 10 );

    count = 0;
    while( mongo_cursor_next( cursor ) == MONGO_OK ) {
        count++;
    }

    ASSERT( count == 10 );

    ASSERT( mongo_cursor_next( cursor ) == MONGO_ERROR );
    ASSERT( cursor->err == MONGO_CURSOR_PENDING );

    mongo_cursor_destroy( cursor );
    remove_sample_data( conn );

    return 0;
}

int test_builder_api( mongo *conn ) {
    int count = 0;
    mongo_cursor cursor[1];

    remove_sample_data( conn );
    insert_sample_data( conn, 10000 );
    mongo_cursor_init( cursor, conn, "test.cursors" );

    while( mongo_cursor_next( cursor ) == MONGO_OK ) {
        count++;
    }
    ASSERT( count == 10000 );

    mongo_cursor_destroy( cursor );

    mongo_cursor_init( cursor, conn, "test.cursors" );
    mongo_cursor_set_limit( cursor, 10 );
    count = 0;
    while( mongo_cursor_next( cursor ) == MONGO_OK ) {
        count++;
    }
    ASSERT( count == 10 );
    mongo_cursor_destroy( cursor );

    return 0;
}

int test_bad_query( mongo *conn ) {
    mongo_cursor cursor[1];
    bson b[1];

    bson_init( b );
    bson_append_start_object( b, "foo" );
        bson_append_int( b, "$bad", 1 );
    bson_append_finish_object( b );
    bson_finish( b );

    mongo_cursor_init( cursor, conn, "test.cursors" );
    mongo_cursor_set_query( cursor, b );

    ASSERT( mongo_cursor_next( cursor ) == MONGO_ERROR );
    ASSERT( cursor->err == MONGO_CURSOR_QUERY_FAIL );
    ASSERT( cursor->conn->lasterrcode == 10068 || cursor->conn->lasterrcode == 16810 );
    ASSERT( strlen( cursor->conn->lasterrstr ) > 0 );

    mongo_cursor_destroy( cursor );
    bson_destroy( b );
    return 0;
}

int test_copy_cursor_data( mongo *conn ) {
    mongo_cursor cursor[1];
    bson b[1];

    insert_sample_data( conn, 10 );
    mongo_cursor_init( cursor, conn, "test.cursors" );

    mongo_cursor_next( cursor );

    ASSERT( bson_copy( b, mongo_cursor_bson( cursor ) ) == MONGO_OK );

    ASSERT( memcmp( (void *)b->data, (void *)(cursor->current).data,
                bson_size( &cursor->current ) ) == 0 );

    mongo_cursor_destroy( cursor );
    bson_destroy( b );

    return 0;
}

int main() {

    mongo conn[1];

    INIT_SOCKETS_FOR_WINDOWS;
    CONN_CLIENT_TEST;

    test_multiple_getmore( conn );
    test_tailable( conn );
    test_builder_api( conn );
    test_bad_query( conn );
    test_copy_cursor_data( conn );

    mongo_destroy( conn );
    return 0;
}
