/*
 */

package com.googlecode.objectify.test;

import com.google.appengine.api.datastore.EntityNotFoundException;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.VoidWork;
import com.googlecode.objectify.test.entity.Trivial;
import com.googlecode.objectify.test.util.GAETestBase;
import com.googlecode.objectify.test.util.TestObjectifyFactory;
import com.googlecode.objectify.test.util.TestObjectifyService;
import com.googlecode.objectify.util.Closeable;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import static com.googlecode.objectify.test.util.TestObjectifyService.ds;
import static com.googlecode.objectify.test.util.TestObjectifyService.fact;
import static com.googlecode.objectify.test.util.TestObjectifyService.ofy;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;

/**
 * Tests of defer()
 *
 * @author Jeff Schnitzer <jeff@infohazard.org>
 */
public class DeferTests extends GAETestBase
{
	@BeforeMethod
	public void setUp() throws Exception {
		TestObjectifyService.setFactory(new TestObjectifyFactory());

		fact().register(Trivial.class);
	}

	/** */
	@Test
	public void deferredSaveWithAutogeneratedId() throws Exception {

		Trivial triv = new Trivial("foo", 5);

		try (Closeable root = TestObjectifyService.begin()) {
			ofy().defer().save().entity(triv);
		}
	}

	/** */
	@Test
	public void deferredSaveAndDeleteProcessedAtEndOfRequest() throws Exception {

		Trivial triv = new Trivial(123L, "foo", 5);

		try (Closeable root = TestObjectifyService.begin()) {
			ofy().defer().save().entity(triv);

			// Can load out of session
			assertThat(ofy().load().entity(triv).now(), is(triv));

			// But not the datastore
			try {
				ds().get(null, Key.create(triv).getRaw());
				assert false : "Entity should not have been saved yet";
			} catch (EntityNotFoundException e) {
				// correct
			}
		}

		try (Closeable root = TestObjectifyService.begin()) {
			Trivial loaded = ofy().load().entity(triv).now();
			assertThat(loaded, equalTo(triv));
		}

		try (Closeable root = TestObjectifyService.begin()) {
			ofy().defer().delete().entity(triv);

			// Deleted in session
			assertThat(ofy().load().entity(triv).now(), nullValue());

			// But not datastore
			try {
				ds().get(null, Key.create(triv).getRaw());
			} catch (EntityNotFoundException e) {
				assert false : "Entity should not have been deleted yet";
			}
		}

		try (Closeable root = TestObjectifyService.begin()) {
			Trivial loaded = ofy().load().entity(triv).now();
			assertThat(loaded, nullValue());
		}
	}

	/** */
	@Test
	public void deferredSaveAndDeleteProcessedAtEndOfTransaction() throws Exception {

		final Trivial triv = new Trivial(123L, "foo", 5);

		try (Closeable root = TestObjectifyService.begin()) {

			ofy().transact(new VoidWork() {
				@Override
				public void vrun() {
					ofy().defer().save().entity(triv);

					// Can load out of session
					assertThat(ofy().load().entity(triv).now(), is(triv));

					// But not datastore
					try {
						ds().get(null, Key.create(triv).getRaw());
						assert false : "Entity should not have been saved yet";
					} catch (EntityNotFoundException e) {
						// correct
					}
				}
			});

			{
				Trivial loaded = ofy().load().entity(triv).now();
				assertThat(loaded, equalTo(triv));
			}

			ofy().transact(new VoidWork() {
				@Override
				public void vrun() {
					ofy().defer().delete().entity(triv);

					// Deleted in session
					assertThat(ofy().load().entity(triv).now(), nullValue());

					// But not datastore
					try {
						ds().get(null, Key.create(triv).getRaw());
					} catch (EntityNotFoundException e) {
						assert false : "Entity should not have been deleted yet";
					}
				}
			});

			{
				Trivial loaded = ofy().load().entity(triv).now();
				assertThat(loaded, nullValue());
			}
		}
	}
}
@@ -195,16 +195,168 @@
 dWork;%0D%0A
+import com.googlecode.objectify.annotation.Entity;%0D%0Aimport com.googlecode.objectify.annotation.Id;%0D%0Aimport com.googlecode.objectify.annotation.OnSave;%0D%0A
 import c
@@ -632,24 +632,45 @@
 Closeable;%0D%0A
+import lombok.Data;%0D%0A
 import org.t
@@ -4288,9 +4288,495 @@
 %09%7D%0D%0A%09%7D%0D%0A
+%0D%0A%09@Entity%0D%0A%09@Data%0D%0A%09static class HasOnSave %7B%0D%0A%09%09@Id private Long id;%0D%0A%09%09private String data;%0D%0A%0D%0A%09%09@OnSave void changeData() %7B%0D%0A%09%09%09data = %22onsaved%22;%0D%0A%09%09%7D%0D%0A%09%7D%0D%0A%0D%0A%09/** */%0D%0A%09@Test%0D%0A%09public void deferredSaveTriggersOnSaveMethods() throws Exception %7B%0D%0A%09%09fact().register(HasOnSave.class);%0D%0A%09%09final HasOnSave hos = new HasOnSave();%0D%0A%0D%0A%09%09try (Closeable root = TestObjectifyService.begin()) %7B%0D%0A%09%09%09ofy().defer().save().entity(hos);%0D%0A%09%09%7D%0D%0A%0D%0A%09%09assertThat(hos.getData(), equalTo(%22onsaved%22));%0D%0A%09%7D%0D%0A%0D%0A
 %7D

Add explicit test for deferred @OnSave