def do_one_search(q, bus, conn, stream, fields, expected_search_keys,
                  terms, results):

    call_create(q, conn, server)

    ret, nc_sig = answer_extended_field_query(q, stream, server, fields)

    path, props = ret.value
    props = unwrap(props)

    assert props[cs.CONTACT_SEARCH_SERVER] == server, pformat(props)
    assert sorted(props[cs.CONTACT_SEARCH_ASK]) == expected_search_keys, \
        sorted(props[cs.CONTACT_SEARCH_ASK])
    assert cs.CONTACT_SEARCH_STATE not in props, pformat(props)

    c = make_channel_proxy(conn, path, 'Channel')
    c_props = dbus.Interface(c, cs.PROPERTIES_IFACE)
    c_search = dbus.Interface(c, cs.CHANNEL_TYPE_CONTACT_SEARCH)

    state = c_props.Get(cs.CHANNEL_TYPE_CONTACT_SEARCH, 'SearchState')
    assert state == cs.SEARCH_NOT_STARTED, state

    # We make a search.
    iq = make_search(q, c_search, c_props, server, terms)
    query = iq.firstChildElement()
    fields_sent = xpath.queryForNodes(
        '/iq/query[@xmlns="%s"]/x[@xmlns="%s"][@type="submit"]/field'
        % (ns.SEARCH, ns.X_DATA), iq)
    assert fields_sent is not None

    # check FORM_TYPE
    f = fields_sent[0]
    assert f['type'] == 'hidden'
    assert f['var'] == 'FORM_TYPE'
    value = f.firstChildElement()
    assert value.name == 'value'
    assert value.children[0] == ns.SEARCH

    # extract search fields
    search_fields = []
    for f in fields_sent[1:]:
        value = f.firstChildElement()
        assert value.name == 'value'
        search_fields.append((f['var'], value.children[0]))

    # Server sends the results of the search.
    send_results_extended(stream, iq, results, fields)

    return search_fields, c, c_search, c_props
def no_x_in_reply(q, bus, conn, stream):
    fields = [('nickname', 'text-single', 'NickName', []),
        ('nick', 'text-single', 'Nick', []),]
    terms = { 'nickname': 'Badger' }

    call_create(q, conn, server)
    ret, nc_sig = answer_extended_field_query(q, stream, server, fields)

    path, _ = ret.value
    c = make_channel_proxy(conn, path, 'Channel')
    c_props = dbus.Interface(c, cs.PROPERTIES_IFACE)
    c_search = dbus.Interface(c, cs.CHANNEL_TYPE_CONTACT_SEARCH)

    iq = make_search(q, c_search, c_props, server, terms)

    # The server sends back an IQ with a <query/> but no <x/> inside the query.
    acknowledge_iq(stream, iq)

    # Gabble should tell us the query failed, and not crash.
    event = q.expect('dbus-signal', signal='SearchStateChanged')
    state = event.args[0]
    assertEquals(cs.SEARCH_FAILED, state)